簡體   English   中英

python遞歸內存不足

[英]python recursion out of memory

當這段代碼運行時,OSX通知我我已經沒有應用程序內存並暫停了應用程序。 Python使用的空間量非常快地打破了10場演出。 這段代碼永遠不會達到Python的最大遞歸級別,它應該只能達到525最壞的情況,但由於緩存它應該小得多。 我有一種感覺,列表鏈正在被每個級別的遞歸復制,但似乎它是一個全局變量,應該與每次調用collat​​z()共享。 我在stackoverflow上找了類似的問題,但我沒有發現任何相同的問題。

# The following iterative sequence is defined for the set of positive integers:
#  n = n/2  (n is even)
#    = 3n+1 (n is odd)
# Using the rule above and starting with 13, we generate the following sequence:
#  13,40,20,10,5,16,8,4,2,1
# It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 
#  terms. Although it has not been proved yet (Collatz Problem), it is thought that
#  all starting numbers finish at 1.
# Which starting number, under one million, produces the longest chain?
# Comments: I'm using dynamic programming to avoid computing the same values over and over
#            again.

lim = 1000000
chain = [1,1]
maxL = 0

def collatz(i):
   if i >= len(chain): chain.extend([None]*(i-len(chain)+1))
   if chain[i] == None: chain[i] = 1 + collatz(i/2 if i%2==0 else 3*i+1)
   return chain[i]

for i in range(1, lim): 
   maxL = i if (collatz(i) > chain[maxL]) else maxL 
   print i, chain[maxL]
print "collatz chain of {} is {} terms long.".format(maxL, collatz(maxL))

編輯:在這里查看我的工作字典實現: https//stackoverflow.com/a/20229855/1084773

要查看內存錯誤,請使用limit = 100運行代碼,然后打印出鏈。

也許你想序列化你的遞歸代碼:

lengths = {1: 1}

def collatz(i):
    i0 = i
    acc = 0
    while True:
        if i in lengths:
            lengths[i0] = acc + lengths[i]
            return acc + lengths[i]
        acc += 1
        i = (i * 3 + 1) if i % 2 else i // 2

longest = 1
for i in range(1, 1000000):
    c = collatz(i)
    if c > longest:
        longest = c
        print(i, c)

這肯定仍然可以通過多種方式進行優化,但它可以在4秒內產生預期的結果。


編輯:

您的方法會創建一個列表,其中包含已達到的最高項的長度。 對於limit = 100,這是9232.這不是那么多。 但是對於limit = 1000000,它是56991483520(從704511開始的鏈),這是一個非常大的。 如果它只是一個超過int32的數組,這已經是212 GB的內存,實際上它不止於此。


麻煩鏈:704511,2113534,1056767,317702,1585151,4755454,2377727,7133182,3566591,10699774,5349887,16049662,8024831,24074494,12037247,36111742,18055871,54167614,27083807,81251422,40625711,121877134,60938567 ,182815702,91407851,274223554,137111777,411335332,205667666,102833833,308501500,154250750,77125375,231376126,115688063,347064190,173532095,520596286,260298143,780894430,390447215,1171341646,585670823,1757012470,878506235,2635518706,1317759353,3953278060 ,1976639030,988319515,2964958546,1482479273,4447437820,2223718910,1111859455,3335578366,1667789183,5003367550,2501683775,7505051326,3752525663,11257576990,5628788495,16886365486,8443182743,25329548230,12664774115,37994322346,18997161173,56991483520,28495741760,14247870880,7123935440 ,3561967720,1780983860,890491930,445245965,1335737896,667868948,333934474,166967237,5500901712,250450856,125225428,62612714,31306357,93919072,46959536,23 479768,11739884,5869942,2934971,8804914,4402457,13207372,6603686,3301843,9905530,4952765,14858296,7429148,3714574,1857287,5571862,2785931,8357794,4178897,12536692,6268346,3134173,9402520,4701260,2350630, 1175315,3525946,1762973,5288920,2644460,1322230,661115,1983346,991673,2975020,1487510,743755,2231266,111536,3346900,1673450,836725,2510176,1255088,627544,313772,156886,78443,235330,117665, 352996,176498,88249,264748,132374,66187,198562,99281,297844,148922,74461,223384,111692,55846,27923,83770,418885,125656,62828,31414,15707,47122,23561,70684,35342, 17671,53014,26507,79522,39761,119284,59642,29821,89464,4432,22366,11183,33550,16775,50326,25163,75490,37745,113236,56618,28309,84928,42464,21232,10616, 5308,2654,1327,3982,1991,5974,2987,8962,4481,1344,6722,3361,10084,5042,2521,7564,3782,1891,5674,2837,8512,4256,2228,1064,532, 266,133,400,200,100,50,25,76,38,19, 58,29,88,44,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1


使用你的確切遞歸的想法,但使用dict(稀疏)而不是列表(運行沒有probs):

lengths = {1: 1}

def collatz(i):
    if i in lengths: return lengths [i]
    j = (i * 3 + 1) if i % 2 else i // 2
    c = collatz (j) + 1
    lengths [i] = c
    return c

longest = 1
for i in range(1, 1000000):
    c = collatz(i)
    if c > longest:
        longest = c
        print(i, c)

列表實現使用了大量的額外內存(正如Hyperboreus指出的那樣,謝謝!)。 切換到字典可以將內存使用量從大約212GB減少到大約70MB,並且可以在2秒內解決問題。

lim = 1000000
chain = {1:1} 
maxL = 1

def collatz(i):
   if i not in chain: chain[i] = 1 + collatz(i/2 if i%2==0 else 3*i+1)
   return chain[i]

for i in range(1, lim): maxL = i if (collatz(i) > chain[maxL]) else maxL 
print "collatz chain of {} is {} terms long.".format(maxL, collatz(maxL))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM