今天一直在尝试实现Rabin-Miller强伪素数测试。
使用了Wolfram Mathworld作为引用,第3-5行总结了我的代码。
然而,当我运行这个程序时,它会说(有时)素数(即使是5,7,11这样的低素数)也不是素数。我看了很长一段时间代码,不知道出了什么问题。
为了获得帮助,我查看了这个站点以及许多其他站点,但大多数站点都使用了另一个定义(可能是相同的,但由于我是这种数学的新手,我看不到相同的明显联系)。
我的代码:
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))
这与Mathworld给出的定义有何不同?
我想知道这段代码:
让我们拿
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
都必须通过n
测试才能成为可能的素数。一。对代码的评论
下面我要说的几点在其他的答案中也提到了,但是把它们放在一起似乎很有用。
在本节中
你已经交换了r和s的角色:你实际上已经将n-1分解为2sr。如果要坚持使用MathWorld表示法,则必须在代码的这一部分中交换
r
和s
:排队
变量
i
未使用:通常将此类变量命名为_
。在1和n-1之间选择一个随机基数:
这是它在MathWorld文章中说的,但那篇文章是从数学家的角度写的。事实上,选择基数1是没用的,因为1s=1(modn)会浪费一次试验。类似地,选择基n-1也没用,因为s是奇数,所以(n-1)s=-1(modn)。数学家不必担心试验的浪费,但是程序员需要,所以写下:
(n要使此优化工作,至少需要4,但是我们可以在n=3时在函数顶部返回
True
来轻松地安排,就像您对n=2所做的那样)如其他回复所述,您误解了MathWorld文章。当它说“n通过测试”时,它意味着“n通过了基础a的测试”。素数的区别在于它们通过了对所有碱基的检验。因此,当你发现as=1(modn)时,你应该做的是绕过这个循环并选择下一个要测试的基。
这里有一个优化的机会。我们刚刚计算的值是a20s(modn)。因此,我们可以立即测试它,并为自己节省一个循环迭代:
在计算a2js(modn)的部分中,每个数字都是前一个数字的平方(模n)。当你能把前一个值平方时,从头计算每一个值是浪费的。所以你应该把这个循环写成:
在尝试Miller-Rabin之前,最好先测试一下小素数的可分性。例如,在Rabin's 1977 paper中,他说:
2。修订代码
把这些放在一起:
相关问题 更多 >
编程相关推荐