Область применения списков в Python - Project Euler 007 - PullRequest
1 голос
/ 13 июня 2011

первый вопрос здесь. Я пытаюсь выучить python, пройдя через проект euler, и наткнулся на контрольно-пропускной пункт. Следующий метод (возвращает список простых факторов) отлично работает для одного вызова:

def findPrimeFactors(num, primeFactors = []):
    '''Find the prime factors of an arbitrary positive integer

        input: num to factorize
        returns: a list containing the prime factors of the number
    '''
    pIndex = 2

    while (num >= pIndex):
        if num % pIndex == 0:
            num /= pIndex
            primeFactors.append(pIndex)
            return FindPrimes.findPrimeFactors(num, primeFactors)

        else:
            pIndex += 1

    return primeFactors

Однако, когда я использую его в цикле, вот так (этот метод может быть еще не завершен, в настоящее время приводит к бесконечному циклу, так как больше простых чисел не может быть найдено):

def countPrimes(n = 1001):
    '''find n amount of unique primes ascending

        input: number of primes to find
        returns: list of n primes starting from 2   '''

    primes = []
    i = 2

    while len(primes) < n:
        primeFactors = FindPrimes.findPrimeFactors(i)
        print(primeFactors) #verify method behavior

        if len(primeFactors) is 1:
            primes.append(primeFactors[0])   
        i += 1

    return primes

В результате первый цикл возвращает [2], следующий возвращает [2, 3] и т. Д., Добавляя новые результаты в список, который, как я хотел, был пуст при первом рекурсивном вызове. Кажется, что мой список сохраняется, но я не уверен, почему именно? Я также прочитал Область и списки классов Python , что дает мне некоторые подсказки, но рекурсия усложняет его еще больше.

Рекурсивный также означает, что я не могу просто присвоить ему пустой набор. Исходя из опыта C ++, я ожидал, что переменная primeFactors должна быть инициализирована каждый раз, когда функция вызывается из моей программы. Все еще змея здесь.

РЕДАКТИРОВАТЬ: Это итерационная версия findPrimeFactors, которую я написал. Я знаю, что это не оптимально, но я хотел бы, по крайней мере, сделать его достаточно эффективным, чтобы соответствовать правилу 1 минуты Project Euler. Любые предложения по улучшению или ясности приветствуются.

PRIMES = [2,3,5,7,11,13,17,19]
import math

class FindPrimes():

    '''V2 iterative'''
    def findPrimeFactors(n, primeFactors = None):
        '''Find the prime factors of an arbitrary positive integer

            input: num to factorize
            returns: a list containing the prime factors of the number
        '''

        if primeFactors is None:
            primeFactors = []

        num = n
        ceil = math.sqrt(n) #currently unused

        global PRIMES
        knownPrimes = PRIMES

        #check known primes for divisors first, then continue searching for primes by brute force
        while True:

            factorFound = False
            for prime in knownPrimes:   

                if num % prime == 0:
                    primeFactors.append(prime)
                    num /= prime
                    factorFound = True
                    break       #ensure that the list returned has ascending primes

            if not factorFound:
                break

        #once attempts have been made to reduce using known primes
        #search for new primes if the number is not fully reduced

        i = knownPrimes[-1] + 2

        while num != 1:

            if num % i == 0:
                knownPrimes.append(i)
                primeFactors.append(i)
                num /= i

            i += 2          

        return primeFactors


    def countPrimes(n = 10001):
        '''find n amount of unique primes ascending

            input: number of primes to find
            returns: list of n primes starting from 2   '''

        primes = []
        i = 2

        while len(primes) < n:

            primeFactors = FindPrimes.findPrimeFactors(i)

            if len(primeFactors) == 1:
                primes.append(primeFactors[0])
                #print(primeFactors[-1])

            i += 1

        print(len(primes))
        return primes

nth = 10001
print(FindPrimes.countPrimes(nth)[nth-1])   #print the largest prime found

Ответы [ 3 ]

3 голосов
1 голос
/ 13 июня 2011

Как упоминалось в Hammar, значение по умолчанию создается только один раз, когда функция определена, и совместно используется вызовами.

Обычный способ - использовать значение маркера в качестве значения по умолчанию:

def findPrimeFactors(num, primeFactors=None):
    if primeFactors is None:
        primeFactors = []
    ...

Не по теме, но ваша функция findPrimeFactor() будет повторяться один раз для каждого найденного простого фактора.Python не выполняет удаление хвостовых вызовов, поэтому вам, вероятно, следует переписать его, используя итерацию, а не рекурсию.

1 голос
/ 13 июня 2011

Значение по умолчанию primeFactors распределяется между вызовами, поэтому при его изменении оно остается неизменным для будущих вызовов.

Пример:

def foo(bar = []):
    bar.append(1)
    return bar

print foo()
print foo()

Выход:

[1]
[1, 1]

Вы должны вернуть новый список вместо изменения по умолчанию:

def foo(bar = []):
    return bar + [1]

print foo()
print foo()

Выход:

[1]
[1]
...