例如
f(2)->1
f(3)->2
f(4)->-1 //4 is not a prime
f(5)->3
...
通常,在达到x
之前制作一个素数发生器并计数def f(x):
p = primeGenerator()
count=1
while True:
y = next(p)
if y>x:
return -1
elif y==x:
return count
else:
count+=1
是不是太慢了?虽然我可以缓存下一个调用的列表,如果我保证输入必须是素数,所以不必测试输入数是否为素数,是否有更快的公式得到答案?
答案 0 :(得分:3)
最好的方法取决于您获得的输入,以及该函数是多次调用还是仅调用一次或几次。
如果经常调用它,并且你要接收的所有输入都很小,不大于10 7 ,那么最好的方法是提前创建一个查找表,然后看看输入。
如果不经常调用,并且所有输入都很小,只需生成不超过输入的素数并计算它们当然就足够了。它可能是一个增强功能,可以记住你下次调用已有的内容,这样当第一个参数是19394489,下一个是20889937时,你不需要再次从0开始,但只需要找到它们之间的素数。他们。但额外的存储是否值得拥有取决于传递的参数。
如果它经常被调用并且参数不是太大,不超过10 13 ,那么最好的方法是为π(n)
的某些选择值预先计算n
的值{1}},并为每个参数查找下一个较小的预计算点的值,然后生成并计算该点与目标值之间的素数(或者如果目标更接近下一个更大的预计算点,则计算目标和那之间的素数。
如果您计算,例如对于10 7 的所有倍数π(n)
,不超过10 13 ,你得到一个包含一百万个条目的查找表,这对现在的内存并不是很费力,并且永远不需要筛选超过500万的范围,这不需要很长时间。
您还可以将查找表作为磁盘上的文件或数据库,这样可以在预先计算的点之间留出更短的间隔。这也将消除启动时在预计算表中读取的时间,但查找现在将涉及对文件系统的访问,这比内存读取花费的时间长得多。什么是最好的策略取决于预期的输入和它运行的系统。
如果上限不小,计算查找表将花费相当长的时间,但这是一次性成本。
如果预期输入较大,最多10 16 ,并且您不愿意花费必要的时间来预先计算该范围的查找表,最好的办法是实现一个对于素数计数函数的更好算法,Meissel's method as refined by Lehmer相对容易实现(不是那么容易,我将在这里给出一个示例实现,但here's a Haskell implementation可能有帮助)。更好,但更复杂的是由Miller et al改进的方法。
除此之外,你需要研究当前最先进的技术,并且可能应该使用比Python更低级的语言。
答案 1 :(得分:2)
你必须检查所有前面的候选人的素数。没有捷径。正如你所说,你可以缓存先前计算的结果并从那里开始,但这是你能做的最好的。