2013-04-18 62 views
8

我目前正在通過項目歐拉的問題,到目前爲止我已經想出了這個問題的代碼。有沒有辦法避免這種內存錯誤?

from itertools import combinations 
import time 

def findanums(n): 
    l = [] 
    for i in range(1, n + 1): 
     s = [] 
     for j in range(1, i): 
      if i % j == 0: 
       s.append(j) 
     if sum(s) > i: 
      l.append(i) 
    return l 

start = time.time() #start time 

limit = 28123 

anums = findanums(limit + 1) #abundant numbers (1..limit) 
print "done finding abundants", time.time() - start 

pairs = combinations(anums, 2) 
print "done finding combinations", time.time() - start 

sums = map(lambda x: x[0]+x[1], pairs) 
print "done finding all possible sums", time.time() - start 

print "start main loop" 
answer = 0 
for i in range(1,limit+1): 
    if i not in sums: 
     answer += i 
print "ANSWER:",answer 

當我運行這個我碰到一個MemoryError

回溯:

File "test.py", line 20, in <module> 
    sums = map(lambda x: x[0]+x[1], pairs) 

我試圖阻止它從什麼我已經能夠從谷歌,但無濟於事得到禁止垃圾收集。我以錯誤的方式接近這個嗎?在我的腦海裏,這感覺就像是最自然的做法,而且我現在處於虧損狀態。

SIDE注意:我使用的是PyPy 2.0 Beta2(Python 2.7.4),因爲它比我用過的其他Python實現快得多,而且Scipy/Numpy在我頭上,因爲我仍然只是開始編程,我沒有工程或強大的數學背景。

+0

你得到了多少內存?系統是64位的? – Ofiris

+0

64位,8 GB的內存,雖然PyPy是32位的,如果這也有所改變。 –

+2

你似乎有某個地方的錯誤。如果在'findanums'運行後'打印len(anums)',它會給出'28123',這意味着從1到28123的_every_數字是一個非常豐富的數字。我不認爲這是正確的。 – Kevin

回答

4

正如凱文在評論中提到的,你的算法可能是錯誤的,但無論如何你的代碼沒有被優化。

當使用非常大名單,這是通常使用generators,有一個著名的,偉大的答案#1解釋yieldgenerator的概念 - What does the "yield" keyword do in Python?

的問題是,你的pairs = combinations(anums, 2)不產生generator ,但會產生一個消耗更多內存的大對象。

我改變你的代碼有這個功能,因爲你遍歷集合只有一次,你可以偷懶評估值:

def generator_sol(anums1, s): 
     for comb in itertools.combinations(anums1, s): 
     yield comb 

的現在,而不是pairs = combinations(anums, 2)產生一個大對象。 用途:

pairs = generator_sol(anums, 2) 

然後,而不是使用lambda我會用另一種generator:在xrange這是更適合

sums_sol = (x[0]+x[1] for x in pairs) 

另一個技巧,你更好看,它好好嘗試生成一個列表但是xrange object在許多情況下更高效(比如這裏)。

+0

現在只是吐出一個錯誤的答案。你已經給了我很多東西來閱讀和學習,所以我感謝你。發電機確實解決了我的記憶問題! –

+2

與歐拉計劃祝你好運。 – Ofiris

+2

範圍不會在pypy中產生一個列表 – fijal

1

問題是anums很大 - 大約有28000個元素。所以配對必須是28000 * 28000 * 8字節= 6GB。如果您使用numpy的,你可以投anums作爲numpy.int16陣列,在這種情況下,結果對將1.5GB - 更易於管理:

import numpy as np 
#cast 
anums = np.array([anums],dtype=np.int16) 
#compute the sum of all the pairs via outer product 
pairs = (anums + anums.T).ravel() 
+1

正如我在我的文章中所說的,我仍然很綠,而且numpy的魔力仍然沒有掌握,因爲我仍然在學習普通的Python庫。但是,我很欣賞這個答案,因爲它讓我嚐到了一旦我學會了足夠的使用numpy/scipy後能夠做的事情! –

2

讓我建議你使用generators。嘗試改變這一點:

sums = map(lambda x: x[0]+x[1], pairs) 

sums = (a+b for (a,b) in pairs) 

Ofiris解決方案也確定,但暗示itertools.combinations返回一個列表,當它是錯的。如果你打算繼續解決項目歐勒問題,請看itertools documentation

+1

好評,謝謝,修正。 – Ofiris

+0

你是什麼意思的'組合'返回一個列表,當它是錯誤的? –

+1

我說組合返回一個「列表」,它不正確,無論如何修復它。 – Ofiris

相關問題