Miller-Rabin素数测试学习笔记
好几天前看了算导上的Miller-Rabin素数测试算法,今天正好总结一下,写写笔记。
说Miller-Rabin测试以前先说两个比较高效的求a*b% n 和 ab %n 的函数,这里都是用到二进制思想,将b拆分成二进制,然后与a相加(相乘)
// a * b % n
//例如: b = 1011101那么a * b mod n = (a * 1000000 mod n + a * 10000 mod n + a * 1000 mod n + a * 100 mod n + a * 1 mod n) mod n
ll mod_mul(ll a, ll b, ll n) {
ll res = 0;
while(b) {
if(b&1) res = (res + a) % n;
a = (a + a) % n;
b >>= 1;
}
return res;
}
//a^b % n
//同理
ll mod_exp(ll a, ll b, ll n) {
ll res = 1;
while(b) {
if(b&1) res = mod_mul(res, a, n);
a = mod_mul(a, a, n);
b >>= 1;
}
return res;
}
下面开始说Miller-Rabin测试:
费马小定理:对于素数p和任意整数a,有ap ≡ a(mod p)(同余)。反过来,满足ap ≡ a(mod p),p也几乎一定是素数。
伪素数:如果n是一个正整数,如果存在和n互素的正整数a满足 an-1 ≡ 1(mod n),我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数。
Miller-Rabin测试:不断选取不超过n-1的基b(s次),计算是否每次都有bn-1 ≡ 1(mod n),若每次都成立则n是素数,否则为合数。
伪代码:
Function Miller-Rabin (n : longint) :boolean;
begin
for i := 1 to s do
begin
a := random(n - 2) + 2;
if mod_exp(a, n-1, n) <> 1 then return false;
end;
return true;
end;
注意,MIller-Rabin测试是概率型的,不是确定型的,不过由于多次运行后出错的概率非常小,所以实际应用还是可行的。(一次Miller-Rabin测试其成功的概率为3/4)
前边说的伪代码实现很简短,下面还有一个定理,能提高Miller测试的效率:
二次探测定理:
如果p是奇素数,则 x2 ≡ 1(mod p)的解为 x = 1 || x = p - 1(mod p);
可以利用二次探测定理在实现Miller-Rabin上添加一些细节,具体实现如下:
bool miller_rabin(ll n) {
if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11) return true;
if(n == 1 || !(n%2) || !(n%3) || !(n%5) || !(n%7) || !(n%11)) return false;
ll x, pre, u;
int i, j, k = 0;
u = n - 1; //要求x^u % n
while(!(u&1)) { //如果u为偶数则u右移,用k记录移位数
k++; u >>= 1;
}
srand((ll)time(0));
for(i = 0; i < S; ++i) { //进行S次测试
x = rand()%(n-2) + 2; //在[2, n)中取随机数
if((x%n) == 0) continue;
x = mod_exp(x, u, n); //先计算(x^u) % n,
pre = x;
for(j = 0; j < k; ++j) { //把移位减掉的量补上,并在这地方加上二次探测
x = mod_mul(x, x, n);
if(x == 1 && pre != 1 && pre != n-1) return false; //二次探测定理,这里如果x = 1则pre 必须等于 1,或则 n-1否则可以判断不是素数
pre = x;
}
if(x != 1) return false; //费马小定理
}
return true;
}
前边这个算法经过测试还是比较靠谱的,可以用作模板。本菜也找过其他模板,可是有的居然把9测成素数,汗 -_-!
AC_Von 原创,转载请注明出处:http://www.cnblogs.com/vongang/archive/2012/03/15/2398626.html