- 一个数 p p p如果是质数,那么一定满足费马小定理, x p − 1 ≡ 1 ( m o d p ) x^{p-1}\equiv1(mod\ p) xp−1≡1(mod p);
- 对于能满足
条
件
1
条件1
条件1的数
p
p
p,显然不一定都是质数。所以需要进行所谓的二次探测。
即若 p p p是质数且 x 2 ≡ 1 ( m o d p ) x^2\equiv1(mod\ p) x2≡1(mod p),则 x ≡ 1 ( m o d p ) x\equiv1(mod\ p) x≡1(mod p)或 x ≡ p − 1 ( m o d p ) x\equiv p-1(mod\ p) x≡p−1(mod p)。
二次探测证明如下:
x
2
≡
1
(
m
o
d
p
)
x
2
−
1
≡
0
(
m
o
d
p
)
(
x
−
1
)
(
x
+
1
)
≡
0
(
m
o
d
p
)
p
∣
(
x
−
1
)
(
x
+
1
)
\begin{aligned} x^2\equiv1(&mod\ p)\\ x^2-1\equiv&0(mod\ p)\\ (x-1)(x+1&) \equiv0(mod\ p)\\ p|(x-1&)(x+1) \end{aligned}
x2≡1(x2−1≡(x−1)(x+1p∣(x−1mod p)0(mod p))≡0(mod p))(x+1)
∵
\because
∵质数
p
p
p只有
1
1
1和他本身两个因数
∴
\therefore
∴所以必定有
p
∣
x
−
1
p|x-1
p∣x−1或
p
∣
x
+
1
p|x+1
p∣x+1
即
x
−
1
≡
0
(
m
o
d
p
)
x-1\equiv0(mod\ p)
x−1≡0(mod p)或
x
+
1
≡
0
(
m
o
d
p
)
x+1\equiv0(mod\ p)
x+1≡0(mod p)
解得
x
≡
1
(
m
o
d
p
)
x\equiv1(mod\ p)
x≡1(mod p)或
x
≡
p
−
1
(
m
o
d
p
)
x\equiv p-1(mod\ p)
x≡p−1(mod p)。
所以算法流程如下:
- 先用费马小定理进行判断
- 如果 p − 1 p-1 p−1是偶数,则有 x p − 1 ≡ 1 ( m o d p ) ⇒ ( x p − 1 2 ) 2 ≡ 1 ( m o d p ) x^{p-1}\equiv1(mod\ p)\Rightarrow (x^{\frac{p-1}{2}})^2\equiv1(mod\ p) xp−1≡1(mod p)⇒(x2p−1)2≡1(mod p),所以可以不断循环用二次探测判定。
由于Miller_Rabin算法再判断的时候容易出现失误,所以我们最好选择多个
x
x
x进行测试判定。
我们在选择
x
x
x的时候,最好选择质数。显然当我们选择的
x
x
x质数越多,第一判断失误的数
T
T
T也会越大。
有人发现,选择
{
2
,
3
,
7
,
61
,
24251
}
\{2,3,7,61,24251\}
{2,3,7,61,24251}这
5
5
5个数作为底数的时候,在
1
0
16
10^{16}
1016的范围内,只有判断
46856248255981
46856248255981
46856248255981时会出现失误,这个数会被判定为素数,而他是个合数
46856248255981
=
4840261
∗
9680521
46856248255981=4840261*9680521
46856248255981=4840261∗9680521。
也有一篇文章《64位以内Rabin-Miller 强伪素数测试和Pollard rho 因数分解算法的实现》提到,
选取前
k
k
k个素数为基进行测试判定,并用
ψ
k
\psi_k
ψk表示以前
k
k
k个素数为基进行素数判定第一个出现失误的合数,Riesel在1994年给出下表:
ψ
1
=
2
,
047
ψ
2
=
1
,
373
,
653
ψ
3
=
25
,
326
,
001
ψ
4
=
3
,
215
,
031
,
751
ψ
5
=
2
,
152
,
302
,
898
,
747
ψ
6
=
3
,
474
,
749
,
660
,
383
ψ
7
=
341
,
550
,
071
,
728
,
321
ψ
8
=
341
,
550
,
071
,
728
,
321
ψ
9
≤
41
,
234
,
316
,
135
,
705
,
689
,
041
ψ
10
≤
1
,
553
,
360
,
566
,
073
,
143
,
205
,
541
,
002
,
401
ψ
11
≤
56
,
897
,
193
,
526
,
942
,
024
,
370
,
326
,
972
,
321
\begin{aligned} \psi_1&=2,047\\ \psi_2&=1,373,653\\ \psi_3&=25,326,001\\ \psi_4&=3,215,031,751\\ \psi_5&=2,152,302,898,747\\ \psi_6&=3,474,749,660,383\\ \psi_7&=341,550,071,728,321\\ \psi_8&=341,550,071,728,321\\ \psi_9&≤41,234,316,135,705,689,041\\ \psi_{10}&≤1,553,360,566,073,143,205,541,002 ,401\\ \psi_{11}&≤56,897,193,526,942,024,370,326,972,321\\ \end{aligned}
ψ1ψ2ψ3ψ4ψ5ψ6ψ7ψ8ψ9ψ10ψ11=2,047=1,373,653=25,326,001=3,215,031,751=2,152,302,898,747=3,474,749,660,383=341,550,071,728,321=341,550,071,728,321≤41,234,316,135,705,689,041≤1,553,360,566,073,143,205,541,002,401≤56,897,193,526,942,024,370,326,972,321
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define ull unsigned long long
#define ld long double
#define inl inline
#define re register
#define MAXN 10100
using namespace std;
inl ll ksc(ull x,ull y, ll p){return (x*y-(ull)((ld)x/p*y)*p+p)%p;}
inl ll ksm(ll x,ll y,ll p)
{
ll res=1;
for(;y;y>>=1,x=ksc(x,x,p))if(y&1)res=ksc(res,x,p);
return res;
}
inl bool mr(ll x,ll p)
{
if(ksm(x,p-1,p)!=1)return 0;
ll y=p-1,z;
while(!(y&1))
{
y>>=1;z=ksm(x,y,p);
if(z!=1&&z!=p-1)return 0;
if(z==p-1)return 1;
}
return 1;
}
int te_pri[20]={0,2,3,5,7,43,61,24251},te_num=7;
inl bool isprime(ll x)
{
if(x<2)return 0;
for(int i=1;i<=te_num;i++)
if(x==te_pri[i])return 1;
for(int i=1;i<=te_num;i++)
if(!(x%te_pri[i])||!mr(te_pri[i],x))return 0;
return 1;
}
int main()
{
return 0;
}