简介
在(1)中,素性检验使用的是费马小定理,对于待检测的数n,如果an-1≡1(mod n),则认为n是素数,为了运算更快,a不是取随机的,而是取2、3、5、7。这样做不是很严谨,即便随机取a,费马检测对于一些伪素数也会失效,如561【对所有的正整数x,1<x<n,若xn-1≡1(mod n),但是n是一个合数,这样的n成为Carmichael数】。
而Miller-Rabin素性检验,每做一次,出错概率至多为1/4,对于同一个数,多次检验,就能够在概率上保证一个数是素数。如果对一个数n,选25个不同的x进行Miller-Rabin检测,若每次都判定为n是素数,则n是合数的概率小于1015分之一。
算法描述
输入:奇整数n,n=1+2k·q
输出:1(n是素数) 或 0(n是合数)
- 随机选取x,使得1<x<n。
- 令j=0,计算y=xq(mod n)。
- 如果y=n-1,或者y=1且j=0,结束算法,返回1;如果y=1且j>0,进入步骤5(否则,进入步骤4)。
- j=j+1,如果j<k,计算y=y2(mod n),进入步骤3。
- 返回0。
代码
int miller_b(BN n,int t=3)
{
if ((n[1] & 1U) == 0)
return 0;
srand((unsigned)time(NULL));
BN n_1 = { 0 }, q = { 0 }, temp = { 0 };
BN x = { 0 };//x是随机获得的
BN y = { 0 };
int flag = 1;//假设是素数
cpy_b(n_1, n);
n_1[1] = n_1[1] - 1U;//n-1
int bits = getbits_b(n_1);
int k = 0;
cpy_b(q, n_1);
for (int i = 1; i < bits; i++)
{
if (uint32_t(q[1] & 1U) == 0U)
{
k++;
shr_b(q);
}
else
break;
}
for (int times = 0; times < t; times++)//默认做3次检验
{
memset(x, 0U, sizeof(x));
randBN(x, getbits_b(n));
if (cmp_b(x, n))
shr_b(x);
//cout << "x = " << bn2str(x)<<endl;
//cout << "x 有 " << getbits_b(x) << "位" << endl;
//cout << "q= " << bn2str(q) << endl;
//cout << "k= " << k << endl << endl;
modexp_b(x, q, n, y);//y=x^q(mod n)
if ((y[0] == 1U && y[1] == 1U) || !cmp_b(y, n_1))
continue;
for (int j = 1; j < k; j++)
{
modmul(y, y, n, temp);
if (temp[0] == 1U && temp[1] == 1U)
break;
else if (!cmp_b(temp, n_1))
goto prime;
cpy_b(y, temp);
}
flag=0;//是合数
break;
prime://素数
;
}
return flag;
}
运行结果
运行速度并不是很慢,一次十几毫秒。找素数比openssl慢不少,openssl产生一个2048位的素数只需要不超过3秒。