当在Python中作为函数调用时,Erostenes的筛选速度要慢得多
我有两段代码,我写这两段代码都是为了应用埃拉托斯烯筛,将所有素数相加到2000000。第一个块是未包装在任何函数中的原始代码,如下所示:当在Python中作为函数调用时,Erostenes的筛选速度要慢得多,python,python-3.x,math,primes,sieve,Python,Python 3.x,Math,Primes,Sieve,我有两段代码,我写这两段代码都是为了应用埃拉托斯烯筛,将所有素数相加到2000000。第一个块是未包装在任何函数中的原始代码,如下所示: N = 2000000 is_prime = (N + 1) * [True] for candidate in range(2, N + 1): if is_prime[candidate]: print(candidate) for witness in range(2 * candidate, N + 1, ca
N = 2000000
is_prime = (N + 1) * [True]
for candidate in range(2, N + 1):
if is_prime[candidate]:
print(candidate)
for witness in range(2 * candidate, N + 1, candidate):
is_prime[witness] = False
第二段代码将此功能拆分为一个检查素性的函数,然后是一个指定上界的for循环。详情如下:
def is_prime(n):
is_prime = (n + 1) * [True]
for candidate in range(2, int(sqrt(n)) + 1):
if is_prime[candidate]:
for witness in range(2 * candidate, n+1, candidate):
is_prime[witness] = False
return is_prime[n]
for candidate in range(2, LIMIT):
if is_prime(candidate):
print(candidate)
然而,分割成检查素性的函数的代码块的速度是无限慢的。我一辈子都搞不清楚这些代码块之间的区别是什么。我做错了什么?您的第二个实现将列表
保留为本地。在每次函数调用时,它通过将列表初始化为(n+1)*[True]
来“重新启动”计算
因此,通过重新启动工作,在使用第二个实现时,基本上可以完成N倍的工作
编辑:正如@Aaron在评论中正确指出的,您对print()的调用也会使第二个版本变慢
问题
概括起来,存在以下问题:
- 使用函数的实现将重新启动其工作
- 第二个实现执行打印。第一个没有,显然更快
- 作为旁注:您的
is_prime
列表与您的函数具有相同的名称。例如,当使用递归时,这将导致麻烦
改进
作为一个非常简单的改进,您可以尝试(重命名并)将is_prime
列表移动到全局变量中。然后,当使用尚未在列表中的数字调用is_prime(n)时,您可以扩展列表(例如,some_list+=difference*[True]
)并仅计算差值 在此基础上,您可以提到MemorizationEdit:对不起,误解了您关于打印的评论。是的,印刷也有影响。但我想说,考虑到重启所做的额外工作,这是可以忽略不计的。@Aaron,谢谢。用你的评论扩展了我的答案。Flurin,非常感谢你的及时和深入的回复!正如我所说,这是我关于stackoverflow的第一篇文章,如此迅速地得到如此有用的回复是非常令人鼓舞的。然而,正如你所建议的,我在循环外移动了+prime,但它似乎没有对速度产生影响。我还发现,一旦找到最大的素数,非功能代码块似乎将停止尝试访问is_素数列表,而第二个代码块将继续访问它提供的每个证人的列表。我通过打印每个证人的身份证明了这一点。伊森,不客气!不幸的是,这比移动列表要复杂得多。假设您调用的是_素数(4)。然后,您的列表将包含数字2、3、4和5的[…(省略0和1)…真、真、假、真]。如果调用is_prime(10),则需要执行以下步骤:首先,将数字6的列表扩大到10(现在的大小为11)。第二,对于数字6到10,试着用你找到的每一个素数(列表中带True的素数)除以它们。