Область применения списков в Python - Project Euler 007

1

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

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] и т.д., добавляя новые результаты в список, который я Wanted был пуст при первом рекурсивном вызове. Кажется, мой список сохраняется, но я точно не знаю, почему? Я читаю класс и списки классов Python, который дает мне некоторые подсказки, но рекурсия усложняет его больше.

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

EDIT: Это итеративная версия findPrimeFactors, которую я написал. Я знаю, что это не оптимально, но я хотел бы хотя бы сделать его достаточно эффективным, чтобы соответствовать правилу Project Euler 1 минута. Любые предложения по улучшению или ясности оцениваются.

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
  • 1
    Кроме того: в отношении 'len (primeFactors) равен 1', вы не хотите это писать. «is» предназначен для идентификации объекта, и Python не дает никаких гарантий, что этому целому числу соответствует только один целочисленный объект. Например, попробуйте 'len (range (257)) 257'. Просто вместо этого напишите len (primeFactors) == 1.
  • 0
    Кстати, см. Stackoverflow.com/questions/1651154/… для объяснения того, почему это работает так.
Показать ещё 1 комментарий
Теги:
scope
list
primes

3 ответа

3
Лучший ответ

См. "Наименьшее удивление" . и Mutable Default Argument

  • 0
    Очень лаконично, спасибо
1

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

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

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

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

  • 0
    Правильный способ сделать аргументы по умолчанию.
  • 0
    Спасибо, я переписал свой метод, используя итерацию, и включил ваше предложение, используя значение маркера. На самом деле у меня не было выбора, так как мой скрипт потерпел крах, когда я попытался найти 10001 простых чисел. Однако мой метод не соответствует проекту Эйлера "правило 1 минуты"
Показать ещё 4 комментария
1

Значение по умолчанию 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]

Ещё вопросы

Сообщество Overcoder
Наверх
Меню