def primes(n):
if n==2: return [2]
elif n<2: return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
你好,我在http://code.activestate.com/recipes/366178-a-fast-prime-number-list-generator/发现了这个功能而且我被卡住了。我不明白这一点。我认为它使用素数的一些属性,但所有那些单字母变量都是如此神秘。你能否解释一下?
我明白了: mroot是您要检查素数的数字的限制 我知道该函数将列表s更改为0来标记倍数。我也理解最后的列表复制,我理解s。
但是为什么一半?什么是j?什么是米?
你能发一些评论吗?
答案 0 :(得分:5)
逐行细分:
def primes(n):
if n==2: return [2]
此函数返回素数列表<= n
。因此,如果n == 2
,该列表仅包含2.足够简单。
elif n<2: return []
在这里,2号以下没有素数,所以返回一个空列表。
s=range(3,n+1,2)
所以s
是一个从3开始并转到n + 1
的奇数列表。 s
代表筛子 - 这是一种筛分算法,这意味着复合(非素数)数字将从列表中筛选出来。实际上,他们将被“划掉”。有关筛分算法的详细说明,请参见下文。
mroot = n ** 0.5
由于它是筛子,我们可以在达到n
的平方根后停止算法。
half=(n+1)/2-1
这是s长度的明确公式;它可以替换为len(s)
,但是对于大的n值计算可能需要更长的时间。这对于终止算法的某些部分也很有用。
i=0
m=3
我是我们的指数;我只需走过筛子,检查每个值。如果值为0
,则该数字已被“划掉”,因为它不是素数。 m
只是s[i]
在任何特定时刻的价值;后一行保持更新。
while m <= mroot:
if s[i]:
由于s[i]
评估为True
,因此尚未从列表中删除。这意味着它是最好的!所以现在我们必须弄清楚列表中的哪些数字是s[i]
的倍数 - 它们都是非素数,应该从列表中划掉。
j=(m*m-3)/2
s[j]=0
现在开始有趣了。因为筛子不是连续数字的列表,而是奇数列表,我们必须找出s
中我们的素数的倍数。在这种情况下,我们的素数是3
,所以我们需要找到9,15,21,27的索引...(我们不必找到6,12,18 ......因为它们'甚至,所以不在列表中)。这种用于查找索引的特定技术非常聪明,因为作者已经发现,一旦特定素数的所有倍数都被划掉,就可以跳过它们。这意味着我们的素数的第一个未划掉的多数实际上是该素数的平方。 (例如,如果素数为7,7 * 3 = 21且7 * 5 = 35已经被划掉,那么我们必须处理的7的第一个倍数是7 * 7.)感觉,很容易看出s
中9的位置是(9 - 3)// 2(其中//是楼层划分)。
while j<half:
s[j]=0
j+=m
现在它又变得容易了。我们发现9;现在我们必须找到15 = 9 + 3 + 3.由于s
只包含奇数,所以它只是每个数字列表的一半;为了向前跳过6,我们只需要向j
添加3。只要j
小于half
,我们就会这样做 - 换句话说,只要j
小于s
的长度。
i=i+1
m=2*i+3
同样,简单 - i
只是列表的索引,而m
是原始数字的值。 (你可以测试一下,看看为什么:[2 * i + 3 for i in range(10)]
。)
return [2]+[x for x in s if x]
并且 voila - 过滤掉筛子中的零,前置[2]并且你有一个素数列表。
这个算法最令人困惑的事情与作者所采用的快捷方式有关,这使得运行速度更快,但却使基本概念失效。 (事实上,人们可以采取更多的捷径,但这是另一篇文章。)这是一个更简单的筛子,显示了基本的想法:
>>> numbers = range(40)
>>> numbers[1] = 0 # 1 isn't prime
>>> for i in numbers:
... if i:
... for j in range(i + i, len(numbers), i):
... numbers[j] = 0
...
>>> [n for n in numbers if n]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
要拼写出来,第一个数字看起来像这样:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10...]
则...
[0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10...]
[0, 0, 2, 3, 0, 5, 0, 7, 0, 9, 0...]
[0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0...]
等等。
答案 1 :(得分:2)
这是Sieve of Eratosthenes的实现。这需要一些快捷方式 - 例如,不是从'2'开始并且越过所有偶数,它使用范围甚至不生成它们(它从3开始并且仅生成每秒数 - 所以3,5,7等,最多n + 1。
答案 2 :(得分:2)
确实它使用了一些技巧。
half
约占n
的一半。加号和减号就是确保half
实际上是最小整数而不是n
的一半。m
始终是列表创建时的s[i]
,也等于2*i+3
。 (你可以从s
的定义中得到它。 m
始终是您要消除的倍数的当前素数。j
用于消除倍数。它从m
平方的索引开始(因为这是需要划掉的最小数字((m*m-3)/2
撤消上面的公式以获取值,在本例中为m*m
,来自指数)m
开始跨越每个j
列表元素。请注意,由于列表只有奇数s[j+m] == s[j]+2m
,这是可以的,因为我们正在以这种方式跳过偶数倍。i
并相应地更新m
。