Python Eratosthenes筛-X和N之间的素数
我发现这个针对Python的Eratosthenes筛在堆栈溢出上的高度优化的实现。我大致知道它在做什么,但我必须承认,它的工作细节让我摸不着头脑 我仍然想把它用于一个小项目,我知道有一些库可以做到这一点,但我想使用这个函数 这是原件:Python Eratosthenes筛-X和N之间的素数,python,algorithm,numpy,primes,Python,Algorithm,Numpy,Primes,我发现这个针对Python的Eratosthenes筛在堆栈溢出上的高度优化的实现。我大致知道它在做什么,但我必须承认,它的工作细节让我摸不着头脑 我仍然想把它用于一个小项目,我知道有一些库可以做到这一点,但我想使用这个函数 这是原件: ''' Sieve of Eratosthenes Implementation by Robert William Hanks https://stackoverflow.com/questions/2068372/fas
'''
Sieve of Eratosthenes
Implementation by Robert William Hanks
https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/3035188
'''
def sieve(n):
"""Return an array of the primes below n."""
prime = numpy.ones(n//3 + (n%6==2), dtype=numpy.bool)
for i in range(3, int(n**.5) + 1, 3):
if prime[i // 3]:
p = (i + 1) | 1
prime[ p*p//3 ::2*p] = False
prime[p*(p-2*(i&1)+4)//3::2*p] = False
result = (3 * prime.nonzero()[0] + 1) | 1
result[0] = 3
return numpy.r_[2,result]
我试图实现的是修改它,从x开始返回n以下的所有素数,以便:
将返回介于50和100之间的素数。这似乎很容易,我试着替换这两行:
def sieve(x, n):
...
for i in range(x, int(n**.5) + 1, 3):
...
但是由于一个我无法解释的原因,上面的x值对返回的numpy数组没有影响
如何修改sieve以仅返回x和n之间的素数您借用的实现可以从3开始,因为它通过跳过所有偶数来代替筛选出2的倍数;这就是代码中多次出现的2*…的内容。3是下一个素数这一事实也被硬编码在所有地方,但我们暂时忽略它,因为如果你不能通过2的特殊外壳,3的特殊外壳就不重要了 跳过偶数是轮子的一种特殊情况。通过始终递增2,可以跳过筛选2的倍数;通过交替增加2和4,可以跳过2和3的筛选倍数;您可以通过交替递增2、4、2、4、6、2、6……来跳过筛选2、3、5和7的倍数。序列中有48个数字,依此类推。因此,您可以通过首先找到x之前的所有素数,然后构建一个轮子,然后使用这个轮子找到x和n之间的所有素数来扩展这段代码 但这增加了很多复杂性。一旦你超过了7,储存轮子的时间和空间成本就会淹没你的积蓄。如果你的全部目标不是在x之前找到素数,那么在x之前找到素数,这样你就不必找到它们似乎有点愚蠢 最简单的方法是找到n以下的所有素数,然后去掉x以下的素数。最后,您只需做一点小小的更改即可:
primes = numpy.r_[2,result]
return primes[primes>=x]
当然,也有一些方法可以做到这一点,而不必浪费存储空间来存储那些您将要扔掉的初始素数。在这个算法中使用它们会有点复杂,你可能想分段构建数组,然后在运行过程中删除所有当然,也有不同的素数查找算法,它们不需要首先枚举x之前的所有素数。但是,如果你想使用这个算法的实现,那没关系。你借用的实现可以从3开始,因为它取代了通过跳过所有偶数筛选出2的倍数;这就是代码中多次出现的2*…的内容。3是下一个素数这一事实也被硬编码在所有地方,但我们暂时忽略它,因为如果你不能通过2的特殊外壳,3的特殊外壳就不重要了 跳过偶数是轮子的一种特殊情况。通过始终递增2,可以跳过筛选2的倍数;通过交替增加2和4,可以跳过2和3的筛选倍数;您可以通过交替递增2、4、2、4、6、2、6……来跳过筛选2、3、5和7的倍数。序列中有48个数字,依此类推。因此,您可以通过首先找到x之前的所有素数,然后构建一个轮子,然后使用这个轮子找到x和n之间的所有素数来扩展这段代码 但这增加了很多复杂性。一旦你超过了7,储存轮子的时间和空间成本就会淹没你的积蓄。如果你的全部目标不是在x之前找到素数,那么在x之前找到素数,这样你就不必找到它们似乎有点愚蠢 最简单的方法是找到n以下的所有素数,然后去掉x以下的素数。最后,您只需做一点小小的更改即可:
primes = numpy.r_[2,result]
return primes[primes>=x]
当然,也有一些方法可以做到这一点,而不必浪费存储空间来存储那些您将要扔掉的初始素数。在这个算法中使用它们会有点复杂,你可能想分段构建数组,然后在运行过程中删除所有当然,也有不同的素数查找算法,它们不需要首先枚举x之前的所有素数。但是,如果你想使用这个算法的实现,那没关系。因为你现在有兴趣研究 其他算法或其他实现,试试这个。它不使用numpy,但速度相当快。我尝试过一些关于这个主题的变化,包括使用集合,预计算一个低素数表,但是它们都比这个慢
#! /usr/bin/env python
''' Prime range sieve.
Written by PM 2Ring 2014.10.15
For range(0, 30000000) this is actually _faster_ than the
plain Eratosthenes sieve in sieve3.py !!!
'''
import sys
def potential_primes():
''' Make a generator for 2, 3, 5, & thence all numbers coprime to 30 '''
s = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
for i in s:
yield i
s = (1,) + s[3:]
j = 30
while True:
for i in s:
yield j + i
j += 30
def range_sieve(lo, hi):
''' Create a list of all primes in the range(lo, hi) '''
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
ihi = int(hi ** 0.5)
for i in potential_primes():
if i > ihi:
break
#Find first multiple of i: i >= i*i and i >= lo
ilo = max(i, 1 + (lo - 1) // i ) * i
#Determine how many multiples of i >= ilo are in range
n = 1 + (hi - ilo - 1) // i
#Mark them as composite
primes[ilo - lo : : i] = n * [False]
return [i for i,v in enumerate(primes, lo) if v]
def main():
lo = int(sys.argv[1]) if len(sys.argv) > 1 else 0
hi = int(sys.argv[2]) if len(sys.argv) > 2 else lo + 30
#print lo, hi
primes = range_sieve(lo, hi)
#print len(primes)
print primes
#print primes[:10], primes[-10:]
if __name__ == '__main__':
main()
这里有一个链接到我在docstring中提到的,以防你想比较这个程序和那个程序
如有必要,您可以通过删除“消除0”和“消除1”下的循环来稍微改善这一点。我猜如果你避免看偶数,速度可能会稍微快一点;它肯定会占用更少的内存。但是你必须处理2在范围内的情况,我认为你的测试越少,这个东西运行得越快
下面是对该代码的一个小改进:替换
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
与
但是,如果您希望返回纯bool列表,而不是执行枚举来构建整数列表,那么原始形式可能更可取:bool列表对于测试给定数字是否为素数更有用;OTOH,枚举可以用来构建一个集合而不是一个列表。既然您现在对其他算法或其他实现感兴趣,那么试试这个。它不使用numpy,但速度相当快。我尝试过一些关于这个主题的变化,包括使用集合,预计算一个低素数表,但是它们都比这个慢
#! /usr/bin/env python
''' Prime range sieve.
Written by PM 2Ring 2014.10.15
For range(0, 30000000) this is actually _faster_ than the
plain Eratosthenes sieve in sieve3.py !!!
'''
import sys
def potential_primes():
''' Make a generator for 2, 3, 5, & thence all numbers coprime to 30 '''
s = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
for i in s:
yield i
s = (1,) + s[3:]
j = 30
while True:
for i in s:
yield j + i
j += 30
def range_sieve(lo, hi):
''' Create a list of all primes in the range(lo, hi) '''
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
ihi = int(hi ** 0.5)
for i in potential_primes():
if i > ihi:
break
#Find first multiple of i: i >= i*i and i >= lo
ilo = max(i, 1 + (lo - 1) // i ) * i
#Determine how many multiples of i >= ilo are in range
n = 1 + (hi - ilo - 1) // i
#Mark them as composite
primes[ilo - lo : : i] = n * [False]
return [i for i,v in enumerate(primes, lo) if v]
def main():
lo = int(sys.argv[1]) if len(sys.argv) > 1 else 0
hi = int(sys.argv[2]) if len(sys.argv) > 2 else lo + 30
#print lo, hi
primes = range_sieve(lo, hi)
#print len(primes)
print primes
#print primes[:10], primes[-10:]
if __name__ == '__main__':
main()
这里有一个链接到我在docstring中提到的,以防你想比较这个程序和那个程序
如有必要,您可以通过删除“消除0”和“消除1”下的循环来稍微改善这一点。我猜如果你避免看偶数,速度可能会稍微快一点;它肯定会占用更少的内存。但是你必须处理2在范围内的情况,我认为你的测试越少,这个东西运行得越快
下面是对该代码的一个小改进:替换
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
与
但是,如果您希望返回纯bool列表,而不是执行枚举来构建整数列表,那么原始形式可能更可取:bool列表对于测试给定数字是否为素数更有用;OTOH,枚举可以用来建立一个集合而不是一个列表。感谢您的精彩解释。因此,我不会通过只获取X以上的素数来节省任何时间。我可能会研究其他算法或其他实现@多汁的:没错;这个特殊的算法是关于使用N以下的所有素数的知识,或者说,每个素数到N的最大倍数来有效地确定N是否为素数,所以即使你不想要这些素数,你也必须获取它们。不,不,你需要的是-筛到N的sqrt,同时筛选从x到n的偏移段。当sqrtn@WillNess:问题是如何通过这个特定算法的特定实现来找到X和Y之间的素数时,比你所建议的工作要少得多。答案还说,正确的解决方案是从许多不同的素数查找算法中选择一种,可以找到X和Y之间的素数,OP评论说,他将研究其他算法或实现。这个特定算法的特定实现完全符合我评论中链接的答案中描述的更改。它的sqrt已经达到了n,只需要将筛子阵列一分为二。不需要使用任何东西,除了埃拉托斯坦筛。它绝对不需要枚举x之前的所有素数就可以开始生成x之后的素数。感谢您的精彩解释。因此,我不会通过只获取X以上的素数来节省任何时间。我可能会研究其他算法或其他实现@多汁的:没错;这个特殊的算法是关于使用N以下的所有素数的知识,或者说,每个素数到N的最大倍数来有效地确定N是否为素数,所以即使你不想要这些素数,你也必须获取它们。不,不,你需要的是-筛到N的sqrt,同时筛选从x到n的偏移段。当sqrtn@WillNess:问题是如何通过这个特定算法的特定实现来找到X和Y之间的素数时,比你所建议的工作要少得多。答案还说,正确的解决方案是从许多不同的素数查找算法中选择一种,可以找到X和Y之间的素数,OP评论说,他将研究其他算法或实现。这个特定算法的特定实现完全符合我评论中链接的答案中描述的更改。它的sqrt已经达到了n,只需要将筛子阵列一分为二。没有必要使用任何东西,除了时代的筛子 托斯尼斯。它绝对不需要枚举x之前的所有素数就可以开始生成x之后的素数。