费马小定理: 假如p是质数,且(a,p)=1,那么 a^(p-1) ≡1(mod p) 假如p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1。该定理的逆否命题也成立,即a^(n-1)mod n!=1,则n为合数.但是费马定律的逆命题就不一定成立了,比如当a=4,n=15时,4^14mod15=1,但是4不是素数而是合数.
快速模取幂
数论计算中经常出现的一种运算就是求一个数的幂ab对另外一个数n个模的运算,即计算:
ab mod n (a,b,n是正整数)
由于计算机只能表示有限位的整数,所以编程时模取幂的运算要注意值的大小范围,当ab的值超过整数范围时,mod运算便无法进行。
如何解决这个问题,我们引出一个能计算ab mod n的值的有用算法——反复平方法,首先我们必须明确:
d=ab mod n=(…((((a mod n)*a)mod n)*a)mod n…*a)mod n {共b个a}
由此可以引出一个迭代式
d:=a;
for i:=2 to b do
d:=d mod n*a;
d:=d mod n;
时间复杂度为O(b),当b很大时,效率很低。我们可以将b转换为二进制数<bk,bk-1,...,b1,b0>,然后从最低位b0开始,由右至左逐位扫描,每次迭代时,用到下面两个恒等式:
a mod n =(ac)2 mod n bi=0
a+1 mod n =a*(ac)2 mod n bi=1 (0<=c<=b)
其中c为b的二进制数的后缀(bi-1...b0)对应的十进制数,当c成倍增加时,算法保持d=ac mod n不变,直至c=b。
程序实现可如下:
long long result(long long a,long long b,long long m)
{
long long d,t;
d=1;
t=a;
while (b>0)
{
if (b%2==1)
d=(d*t)%m;
b/=2;
t=(t*t)%m;
}
return d;
}
要测试 N 是否为质数,首先将 N-1分解为 2^s d。在每次测试开始时,先随机选一个 介于 【1, n-1】的整数 a,之后如果对所有的 r \in 【0, s-1】,若a^d \mod N 不等于1 且 a^((2^r)*d) mod N不等于 -1,则 N 是合数。否则,N 有 3/4 的机率为质数。
算法模板:
- #include <iostream>
- using namespace std;
- int fmod(int a, int b, int c)//快速模取幂
- {
- if(b == 1) return a;
- int t = fmod(a, b / 2, c);
- t = (t * t) % c;
- if(b & 1) t = (t * a) % c;
- return t;
- }
- int check(int n)//米勒-拉宾算法
- {
- srand(time(0));
- for(int i = 0; i < 100; ++ i)
- {
- if(fmod(rand() % (n - 1) + 1, n - 1, n) != 1)//a的值需要变化,所以用到随机函数;a的取值为[1,n-1],因为这样就会保证如果n为素数,则n与a必互质
- return 0;
- }
- return 1;
- }