2013-04-18 16 views
8

Obecnie pracuję nad problemami w Projekcie Euler i do tej pory wymyśliłem ten kod dla problemu.Czy istnieje sposób na uniknięcie tego błędu pamięci?

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 

Gdy uruchomię to biegnę do MemoryError.

traceback:

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

Próbowałem zapobiec poprzez wyłączenie zbierania śmieci z tego, co udało mi się dostać od Google, ale bezskutecznie. Czy podchodzę do tego w niewłaściwy sposób? W mojej głowie wydaje mi się to najbardziej naturalnym sposobem na zrobienie tego, a ja w tej chwili jestem bezradny.

SIDE UWAGA: Używam PyPy 2.0 Beta2 (Python 2.7.4), ponieważ jest znacznie szybszy niż jakakolwiek inna implementacja Pythona, której używałem, a Scipy/Numpy są nad moją głową, ponieważ wciąż jestem po prostu zaczynają programować i nie mam inżynierii ani silnego tła matematycznego.

+0

Ile pamięci masz? to jest system 64-bitowy? – Ofiris

+0

64-bitowe, 8 GB pamięci, chociaż PyPy ma wartość 32-bitową, jeśli ma również znaczenie. –

+2

Wygląda na to, że masz gdzieś błąd. Jeśli "drukujesz len (anums)" po uruchomieniu 'findanums', to daje' 28123', co oznacza, że ​​_nasta_ liczba od jednego do 28123 jest liczbą mnogą. Nie sądzę, że to prawda. – Kevin

Odpowiedz

4

Jak wspomina Kevin w komentarzach, Twój algorytm może być nieprawidłowy, ale w każdym razie twój kod nie jest zoptymalizowany.

Przy zastosowaniu bardzo dużych list, to jest powszechne w użyciu generators, znajduje się słynny, wielki odpowiedź Stackoverflow wyjaśnianiu pojęć yield i generator - What does the "yield" keyword do in Python?

Problemem jest to, że pairs = combinations(anums, 2) nie generuje generator , ale generuje duży obiekt, który zajmuje znacznie więcej pamięci.

zmieniłem swój kod aby mieć tę funkcję, ponieważ iteracji po kolekcji tylko raz, można leniwy oceny wartości:

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

Teraz zamiast pairs = combinations(anums, 2) która generuje duży obiekt. Zastosowanie:

pairs = generator_sol(anums, 2) 

Następnie, zamiast używania lambda użyłbym innego generator:

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

Kolejna wskazówka, lepiej przyjrzeć xrange który jest bardziej odpowiedni, doens't wygenerować listę ale xrange object, który jest bardziej wydajny w wielu przypadkach (np. tutaj).

+0

Teraz po prostu wypluwa złą odpowiedź. Dałeś mi dużo do przeczytania i nauki, dlatego dziękuję. Generator rzeczywiście rozwiązał mój problem z pamięcią! –

+2

Powodzenia z Projektem Euler. – Ofiris

+2

Zasięg nie generuje listy w pypie ani – fijal

1

Kwestia polega na tym, że anums jest duży - ma około 28 000 elementów. więc pary muszą mieć 28000 * 28000 * 8 bajtów = 6 GB. Jeśli użyto numpy można rzucić anums jako tablica numpy.int16, w którym to przypadku pary wynik byłby 1,5GB - łatwiejsze:

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

Jak już powiedziałem w swoim poście, wciąż jestem całkiem zielony i magia numpy wciąż jest poza moim zasięgiem, ponieważ wciąż uczę się normalnych bibliotek Pythona . Ale doceniam tę odpowiedź, ponieważ daje mi to próbkę tego, co będę w stanie zrobić, gdy nauczę się na tyle, by używać numpy/scipy! –

2

Pozwolę sobie zasugerować, aby korzystać generators. Spróbuj zmienić to:

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

do

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

rozwiązanie Ofiris jest również ok, ale zakłada, że ​​itertools.combinations zwróci listę kiedy to się stało. Jeśli masz zamiar nadal rozwiązywać problemy związane z projektami, spójrz na numer itertools documentation.

+1

Dobry komentarz, dzięki, Naprawiono. – Ofiris

+0

Co masz na myśli przez "kombinacje" zwraca listę, gdy jest źle? –

+1

Powiedziałem, że kombinacja zwraca 'list' i jest niepoprawny, naprawił to tak czy inaczej. Kombinacje – Ofiris

Powiązane problemy