Python 中的列表范围 - Project Euler 007
Posted
技术标签:
【中文标题】Python 中的列表范围 - Project Euler 007【英文标题】:Scope of lists in Python - Project Euler 007 【发布时间】:2011-09-13 16:49:12 【问题描述】:这里的第一个问题。我正在尝试通过逐步完成项目 euler 来学习 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],依此类推,将新结果附加到我希望在第一次递归调用时为空的列表。似乎我的列表仍然存在,但我不确定为什么?我也阅读了Python Class scope & lists,这给了我一些线索,但递归使它更加复杂。
递归也意味着我也不能简单地为它分配一个空集。来自 C++ 背景,我的期望是每次从我的程序调用函数时都应该重新初始化 primeFactors 变量。这里还是一条小蛇。
编辑:这是我编写的 findPrimeFactors 的迭代版本。我知道这不是最优的——但我想至少让它足够高效以满足欧拉计划的 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
【问题讨论】:
旁白:关于 'len(primeFactors) is 1',你不想这样写。 "is" 用于对象标识,Python 不保证只有一个整数对象对应于给定的数字。例如,尝试 'len(range(257)) is 257'。只需写 len(primeFactors) == 1 代替。 顺便说一句,请参阅 ***.com/questions/1651154/… 了解为什么它会这样工作。 该线程使默认值行为的推理更加清晰。我已经根据您的第一个建议修改了我的代码。你的意思是暗示 range(257) 是一个有多个整数对象对应的数字吗? 【参考方案1】:见"Least Astonishment" and the Mutable Default Argument
【讨论】:
【参考方案2】: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]
【讨论】:
【参考方案3】:正如 hammar 所说,默认值仅在定义函数时创建一次,并在调用之间共享。
通常的方法是使用标记值作为默认值:
def findPrimeFactors(num, primeFactors=None):
if primeFactors is None:
primeFactors = []
...
题外话,但您的函数 findPrimeFactor()
将针对找到的每个素数递归一次。 Python 不会删除尾调用,因此您可能应该使用迭代而不是递归来重写它。
【讨论】:
谢谢,我使用迭代重写了我的方法,并使用标记值合并了您的建议。实际上我别无选择,因为当我试图找到 10001 个素数时我的脚本崩溃了。但是我的方法不符合欧拉项目“1分钟规则” 大概是因为达到了python的递归深度限制而失败了?另外,你能详细说明一下尾声吗?来自***:尾调用很重要,因为它们可以在不向调用堆栈添加新堆栈帧的情况下实现。当前程序的大部分框架已经不需要了,可以用尾调用的框架代替,适当修改。然后程序可以跳转到被调用的子程序。生成这样的代码而不是标准调用序列称为尾调用消除或尾调用优化。 这是否意味着 Python 会为我的方法中的每个递归调用创建一个新的堆栈帧? 是的,这正是它的意思,Python(默认情况下)只允许你有 1000 个堆栈帧。您可以更改最大堆栈大小,但最好重新设计您的算法。 @kindall 感谢@RemyBlank 的澄清我在我的代码中包含了 findPrimeFactors 的迭代版本 - 我正在努力改进它以上是关于Python 中的列表范围 - Project Euler 007的主要内容,如果未能解决你的问题,请参考以下文章