根据费马的小定理解释检查素性的代码

时间:2015-04-12 23:25:08

标签: python math primes prime-factoring

我找到了一些声称根据Fermat's little theorem检查素性的Python代码:

def CheckIfProbablyPrime(x):
    return (2 << x - 2) % x == 1

我的问题:

  1. 它是如何运作的?
  2. 它与费马小定理的关系是什么?
  3. 这种方法有多准确?
  4. 如果不准确,使用它有什么好处?
  5. 我发现它here

2 个答案:

答案 0 :(得分:15)

1。它是如何工作的?

Fermat's little theorem说如果数字 x 是素数,那么对于任何整数 a

Fermat's Little Theorem Part 1

如果我们用 a 划分双方,那么我们可以按如下方式重新编写等式:

Fermat's Little Theorem Part 2

我将试图证明 这是如何工作的(你的第一个问题),因为在this wiki page和一些谷歌搜索下有很多好的证明(比我能提供的好)

2。代码与定理之间的关系

因此,您发布的功能会检查(2 << x - 2) % x == 1

首先,(2 << x-2)与撰写2**(x-1)或数学形式相同:

2**(x-1)

那是因为<<logical left-shift operator,更好地解释here。位移和乘以2的关系之间的关系特定于数字在计算机上的表示方式(二进制),但都归结为

2 << (x-1) == 2**(x)

我可以从两边的指数中减去1,这给出了

2 << (x-2) == 2**(x-1)


现在,我们从上面知道任何数字

a**(x-1) == 1 (mod x)

然后我们说 a = 2 。这给了我们

2**(x-1) == 1 (mod x)

嗯,这跟2 << (x-2)一样!那么我们可以写:

Almost final relation

这导致最终的关系:

Final Relation


现在,mod的数学版本看起来很奇怪,但我们可以编写如下的等效代码:

(2 << x - 2) % x == 1

这就是关系。

3。方法的准确性

所以,我认为“准确性”在这里是一个不好的术语,因为费马的小定理对于所有素数都是正确的。但是,表示所有数字都是真或假 - 也就是说,如果我有一些数字 i ,我不确定是否< em> i 是素数,使用Fermat的Little Relation只会告诉我它是否绝对不是素数。如果费马的小关系是真的,那么 i 就不能成为素数。这些类型的数字称为pseudoprime numbers,或者更具体地说,在这种情况下称为Fermat Pseudoprime数字。

如果这类事情听起来很有意思,请看一下Carmichael numbers AKA绝对费马假鹦鹉,它在任何基础上通过费马测试,但不是素数。在我们的例子中,我们遇到了数字,这些数字在基数2中通过,但费马的小定理可能不适用于其他基数中的这些数字 - 卡迈克尔数字将所有基数的测试通过x

在Carmichael的wiki页面上讨论了它们在自然数范围内的分布 - 它们以你所看到的范围的大小呈指数级显示,尽管指数小于1(约1/3)。所以,如果你在大范围内搜索质数,那么你将会出现指数级更多的卡迈克尔数,这对于这种方法CheckIfProbablyPrime实际上是误报。这可能没问题,这取决于你的输入以及你对误报的影响程度。

4。为什么这有用?

简而言之,这是一种优化。

使用这样的东西的主要原因是加快搜索素数。这是因为实际检查数字是否为素数是昂贵的 - 即超过O(1)运行时间。可行,但仍比O(1)时间更昂贵。因此,如果我们可以避免对某些数字进行实际检查,我们将能够投入更多时间来检查实际候选人。因为Fermat的小关系只会说一个数字可能是素数(如果数字是素数就永远不会说不),并且可以在O(1)时间内检查,我们可以把它扔进is_prime循环忽略相当数量的数字。所以,我们可以加快速度。

有许多像这样的素数检查,你可以找到一些编码的素数检查器here


最后的注释

这种优化的一个令人困惑的事情是它使用位移运算符<<而不是指数运算符**。这是因为位移是计算机可以执行的最快的操作之一,而取幂则会慢一些。在许多情况下它是not always the best optimization,因为大多数现代语言都知道如何用更优化的操作替换我们编写的东西。但是,我的冒险是为什么此代码的作者使用了位移而不是2**(x-1)


编辑:正如MarkDickinson指出的那样,取一个数字的指数然后明确地修改它并不是最好的方法。这是一个名为modular exponentiation的东西,并且存在比我们编写它的方式更快的算法。 Python的内置pow实际上实现了这些算法之一,并将可选的第三个参数带到mod by。所以我们可以编写这个函数的最终版本:

def CheckIfProbablyPrime(x):
    return pow(2, x-1, x) == 1

这不仅比可疑的比特换档更具可读性,而且更快You know what they say.

答案 1 :(得分:3)

我相信,你的例子中的代码是不正确的,因为二进制左移运算符不等于数字的幂,这在费马的小定理中使用。以2为基数,二进制左移将等于x + 1的幂,而不是在Fermat的小格式版本中使用。

相反,在Python中使用**作为整数幂。

def CheckIfProbablyPrime(x):
    return (2 ** x - 2) % x == 0

&#34; p - a是p&#34;的整数倍。因此对于素数,按照定理,x = 2除以x的2的结果将留下0的剩余(模数&#39;%&#39;检查除法后留下的数字。

对于x - 1版本,

def CheckIfProbablyPrime(a, x):
   return (a ** (x-1) - 1) % x == 0

这两种变化对于素数都应该是正确的,因为它们代表了Fermat在Python中的小定理