源代码:
typedef long int HugeInt;
HugeInt Witness(HugeInt A, HugeInt i, HugeInt N)
{
HugeInt X, Y;
if(i == 0)
return 1;
X = Witness(A, i/2, N);
if(X == 0)
return 0;
Y = ( X * X) % N;
if(Y == 1 && X !=1 && X != N-1)
return 0;
if(i % 2 != 0)
Y = (A * Y) % N;
return Y;
}
bool IsPrime(HugeInt N)
{
return Witness(RandInt(2, N-2), N - 1, N) == 1;
}
原理:
1. 费马小定理:如果N是素数,且0 < A < N,那么A^(N-1) mod N = 1
由上述定理可知,如果A^(N-1) mod N != 1,则N为非素数。
但是如果A^(N-1) mod N = 1,我们不能推导出N为素数。
例如:N=341,但满足上述公式,但341为非素数。
A=3, N = 561, 满足上式,但561非素数。由此可见,不恰当的A也可能导致算法出错。
2. 定理:如果N是素数,且0 < X < N,那么X^2 mod N = 1仅有的两个解为X=1, X = N-1。
证明: X^2 - 1 mod N = 0
(X+1)(X-1) mod N = 0
由于N是素数,只有因子1与N,所以N要么整除X+1,要么整除X-1。因为0 < X < N,所以X=1或者X=N-1时有解。
因此,如果X^2 mod N = 1时,X不为1或者N-1,则说明N为非素数。
3. 总结:使用定理1来验证一个大数是否为素数(A^(N-1) mod N = 1),为了减少误差,在计算的时候使用定理2进行测试,如果
出现不满足情况,则终止测试,返回非素数。
但这样并只能减少误差,不能消除错误,所以,可以通过独立随机试验减少错误发生概率。
试验证明,如果随机选取A,该算法有75%的正确率,即错误率为25%。这样,如果我们进行50次试验,误差不大于1/4^50。
代码:
1. 本算法用到了递归求幂算法
2. 定理2的运用
Y = ( X * X) % N;
if(Y == 1 && X !=1 && X != N-1)
return 0;
当X^2 mod N = 1时,X !=1 或X!=N-1,则说明该数为非素数,检查可以终止。
3. 递归求幂 幂为奇数
if(i % 2 != 0)
Y = (A * Y) % N
当幂为奇数的时候,要多乘一次A
4. 实质是递归求幂的反过程
if(i == 0)
return 1;
Witness(A, i/2, N);
Y = ( X * X);if(i % 2 != 0) Y = (A * Y);
return Y;
只看上述代码,其实就是递归求幂的过程。
5. 费马小定理
Witness求A^(N-1) mod N。如果返回值为1,则以很大的概率认为该数为素数。
6. %N的作用
(x * y) % N = ( (x % N) * ( y % N) ) % N
所以,在各个阶段计算的过程中取余,对最终的结果是没有影响的。即如果A^(N-1) mod N = 1,那么用各个计算
的中间结果去mod N,对最终结果是没有影响的。
而同时Y = ( X * X) % N计算,正好方便了定理2的使用。