Python Rabin-Miller强伪素数测试实现赢得';行不通
今天我们一直在尝试实现Rabin-Miller强伪素数测试 作为参考,第3-5行总结了我的代码 然而,当我运行程序时,它会说(有时)素数(即使是低的,如5,7,11)不是素数。我已经看了很长一段时间的代码,不能找出什么是错误的 为了寻求帮助,我查看了这个网站以及许多其他网站,但大多数网站使用了另一个定义(可能是相同的,但由于我对这种数学不熟悉,我看不到相同的明显联系) 我的代码:Python Rabin-Miller强伪素数测试实现赢得';行不通,python,algorithm,math,Python,Algorithm,Math,今天我们一直在尝试实现Rabin-Miller强伪素数测试 作为参考,第3-5行总结了我的代码 然而,当我运行程序时,它会说(有时)素数(即使是低的,如5,7,11)不是素数。我已经看了很长一段时间的代码,不能找出什么是错误的 为了寻求帮助,我查看了这个网站以及许多其他网站,但大多数网站使用了另一个定义(可能是相同的,但由于我对这种数学不熟悉,我看不到相同的明显联系) 我的代码: import random def RabinMiller(n, k): # obviously not
import random
def RabinMiller(n, k):
# obviously not prime
if n < 2 or n % 2 == 0:
return False
# special case
if n == 2:
return True
s = 0
r = n - 1
# factor n - 1 as 2^(r)*s
while r % 2 == 0:
s = s + 1
r = r // 2 # floor
# k = accuracy
for i in range(k):
a = random.randrange(1, n)
# a^(s) mod n = 1?
if pow(a, s, n) == 1:
return True
# a^(2^(j) * s) mod n = -1 mod n?
for j in range(r):
if pow(a, 2**j*s, n) == -1 % n:
return True
return False
print(RabinMiller(7, 5))
随机导入
def拉宾米勒(n,k):
#显然不是质数
如果n<2或n%2==0:
返回错误
#特例
如果n==2:
返回真值
s=0
r=n-1
#因子n-1作为2^(r)*s
当r%2==0时:
s=s+1
r=r//2#层
#k=精度
对于范围(k)内的i:
a=随机。随机范围(1,n)
#a^(s)模n=1?
如果功率(a,s,n)=1:
返回真值
#a^(2^(j)*s)模n=-1模n?
对于范围(r)内的j:
如果功率(a,2**j*s,n)=-1%n:
返回真值
返回错误
印刷品(拉宾米勒(7,5))
这与Mathworld给出的定义有何不同?我想知道这段代码:
# factor n - 1 as 2^(r)*s
while r % 2 == 0:
s = s + 1
r = r // 2 # floor
让我们取n=7
。所以n-1=6
。我们可以将n-1
表示为2^1*3
。在这种情况下,r=1
和s=3
但是上面的代码发现了其他东西。它以r=6
开头,因此r%2==0
。最初,s=0
因此在一次迭代之后,我们得到了s=1
和r=3
。但是现在r%2!=0,循环终止
我们最终得到了s=1
和r=3
,这显然是错误的:2^r*s=8
您不应该在循环中更新s
。相反,您应该计算可以被2除多少次(这将是r
),并且除法后的结果将是s
。在n=7
,n-1=6
的例子中,我们可以将其除以一次(因此r=1
),然后除以3(因此s=3
)。除了Omri Barel所说的,for循环也有问题。如果找到通过测试的a
,您将返回true
。但是,所有的a
都必须通过测试才能成为可能的素数。以下是我的版本:
# miller-rabin pseudoprimality checker
from random import randrange
def isStrongPseudoprime(n, a):
d, s = n-1, 0
while d % 2 == 0:
d, s = d/2, s+1
t = pow(a, d, n)
if t == 1:
return True
while s > 0:
if t == n - 1:
return True
t, s = pow(t, 2, n), s - 1
return False
def isPrime(n, k):
if n % 2 == 0:
return n == 2
for i in range(1, k):
a = randrange(2, n)
if not isStrongPseudoprime(n, a):
return False
return True
如果你想了解更多关于素数编程的知识,我在我的博客上谦虚地推荐你。1。对代码的注释
我将在下面提出的一些观点在其他答案中有所提及,但将它们放在一起似乎很有用
在节中
s=0
r=n-1
#因子n-1作为2^(r)*s
当r%2==0时:
s=s+1
r=r//2#层
你已经交换了r和s的角色:你实际上已经考虑了n− 1作为2sr。如果要坚持使用MathWorld符号,则必须在代码的这一部分中交换r
和s
:
#因子n-1为2^(r)*s,其中s为奇数。
r、 s=0,n-1
当s%2==0时:
r+=1
s/=2
排队
for i in range(k):
变量i
未使用:通常将此类变量命名为\u
在1和n之间选择一个随机基数− 1包括:
a = random.randrange(1, n)
这是MathWorld文章中所说的,但那篇文章是从数学家的角度写的。事实上,选择基数1是无用的,因为1s=1(mod n),您将浪费一次试验。同样,选择基数n也没用− 1,因为s是奇数,所以(n− 1) s=−1(模块n)。数学家不必担心试验的浪费,但程序员需要担心,所以写下:
a = random.randrange(2, n - 1)
(n需要至少为4才能进行优化,但我们可以通过在n=3时在函数顶部返回True
来轻松安排,就像在n=2时一样。)
如其他回复所述,您误解了MathWorld的文章。当它说“n通过了测试”时,它意味着“n通过了基础a的测试”。关于素数的区别在于,它们通过了所有基的测试。因此,当您发现as=1(mod n)时,您应该做的是遍历循环并选择下一个要测试的基数
# a^(s) = 1 (mod n)?
x = pow(a, s, n)
if x == 1:
continue
这里有一个优化的机会。我们刚刚计算的x值是20秒(mod n)。因此,我们可以立即对其进行测试,并节省一次循环迭代:
# a^(s) = ±1 (mod n)?
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
在计算a2j s(模n)的部分中,这些数字中的每一个都是前一个数字(模n)的平方。如果只需将上一个值平方,就可以从头开始计算每个值,这是一种浪费。因此,您应该将此循环编写为:
# a^(2^(j) * s) = -1 (mod n)?
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
在尝试米勒-拉宾之前,最好先测试小素数的可除性。例如,他在书中说:
在实现该算法时,我们加入了一些节省人力的步骤。首先,我们测试任何素数p
2.修订守则
综上所述:
来自随机导入范围
小素数=[2,3,5,7,11,13,17,19,23,29,31]#等。
def_prime(n,k):
“”“若n通过Miller-Rabin素性的k轮,则返回True。”
测试(可能是素数)。如果n被证明为
混合成的
"""
如果n<2:返回False
对于小素数中的p:
如果n