Python Rabin-Miller强伪素数测试实现赢得';行不通

Python Rabin-Miller强伪素数测试实现赢得';行不通,python,algorithm,math,Python,Algorithm,Math,今天我们一直在尝试实现Rabin-Miller强伪素数测试 作为参考,第3-5行总结了我的代码 然而,当我运行程序时,它会说(有时)素数(即使是低的,如5,7,11)不是素数。我已经看了很长一段时间的代码,不能找出什么是错误的 为了寻求帮助,我查看了这个网站以及许多其他网站,但大多数网站使用了另一个定义(可能是相同的,但由于我对这种数学不熟悉,我看不到相同的明显联系) 我的代码: import random def RabinMiller(n, k): # obviously not

今天我们一直在尝试实现Rabin-Miller强伪素数测试

作为参考,第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))
随机导入
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