遇到一個情況,需要進行遞歸操作,但是呢遞歸次數非常大,有一萬多次。先不說一萬多次遞歸,原來的測試程式碼是java的,沒裝jdk和編譯環境,還是用python吧
先看下原本的java程式碼:
public class UpCount { private long calc(int depth) { if (depth == 0) return 1; long cc = calc(depth - 1); return cc + (depth % 7) + ((((cc ^ depth) % 4) == 0) ? 1 : 0); } public static void main(String[] args) { UpCount uc = new UpCount(); System.out.println(uc.calc(11589)); } }
java沒怎麼玩過,但是這幾行程式碼看過來還是沒壓力的,快刀斬亂麻改為對於python代碼
def calc(depth): if depth == 0: return 1 cc = long(calc(depth-1)) xor_mod = (cc ^ depth)%4 if xor_mod == 0: return cc+(depth%7)+1 else: return cc+(depth%7) number = long(calc(11589)) print number
代碼粘上去,F5,出錯了
這個版本的代碼本來是沒有加long因為之前一串十幾位的整數直接拿來就可以用,所以懷疑跟long是不是有關係
當然啦,事實上這裡跟long完全沒關係,python支持的整數長度可是非常長的,參考之前寫的程式碼如下:
cimal = 7 original = 28679718602997181072337614380936720482949 array = "" result= "" while original !=0: remainder = original % cimal array += str(remainder) original /= cimal length = len(array) for i in xrange(0,length): result += array[length-1-i] print result
因此,可以看出來,出錯不在於數的大小,畢竟11589對現在的計算機來說只是小菜,2^16還有65536呢
其實到這裡才發現,沒有說前面遞歸報錯的真正原因,憔悴了
遞歸出錯的原因是因為python默認的遞歸限制只有1000次左右,但是這裡卻要運行10000+,刷了半天:RuntimeError: maximum recursion depth exceeded
於是趕緊查了下,發現可以自己設定遞歸的限制,見python中遞歸的最大次數,作為延伸也可以查看官網文檔
總的說來就是,為了防止益處和崩潰,python語言預設對次數加了限制,那麼我改了這個限制是不是就ok了呢
import sys
# set the maximun depth as 20000
sys.setrecursionlimit(20000)
,20000果斷改20000,這下沒這限制應該沒問題了,但是結果卻大跌眼鏡,什麼都沒輸出來,不解了沒有繼續查了,問了下小伙伴littlehann,討論了下, 沒有對這個問題深究下去。而是提到遞歸這種運算在實際應用上的效率,確實除了課本上很少看到使用遞歸的本來的目的就只是求值,沒想對它深究下去,還是改用迭代吧,雖然沒太大印象了,不過一個for語句據可以搞定了
程式碼如下:
def calc(depth): tmp = 0 result = 1 for i in xrange(0,depth+1): cc = result if (cc ^ i)%4 == 0: tmp = 1 else: tmp = 0 result = result + (i)%7 + tmp return result final = calc(11589) print final
短短幾行程式碼,一下子搞定了。想到上次面試的時候,tx的面試官問我演算法,當時提到了用遞歸實作一個運算,再想想是不是也可以用迭代呢?
時間過去很久了,當時的題目也記不太清楚了,但是今天的教訓是:大多數(代碼寫得少,憑感覺說的估計值)情況下,遞歸的效率是比較低下的,這一點可以確定,上課的時候也有講過。使用迭代的效率明顯要高過遞歸(迭代的具體概念記不太清楚了),起碼用循環,運算幾十萬次我可以肯定沒問題,但是即便我改了遞歸限制,還是遇到了罷工
最後,再貼出一個python long VS C long long的鏈接,有興趣的可以去看看