[miller_rabin]
miller_rabin
log(n)级别复杂度的判断素数的方式
费马小定理
对于任意整数\(a,p\)互质,则有$$a^{p-1}≡1(mod~p)$$
那么思考反过来可不可以?如果有\(a<p\)且满足这个式子,能不能说明\(p\)是质数呢?很遗憾,不可以,有些合数是可以满足这个式子的,它们被称为费马伪素数,最小的费马为素数是341,那么我们考虑既然有某个数\(a\)可以让费马伪素数\(p\)成立这个式子,那么把限制扩大,如果不是某个数\(a\)满足而是要求有多个数\(a\)都能使得\(p\)成立,那么能不能说明\(p\)是质数?通过多选取\(a\)的方式确实能筛选掉很多,但是不能完全筛掉,即遍历完\([2,p-1]\)也不能把它筛去,这样的合数称为卡迈尔数,而若\(n\)为卡迈尔数则\(2^n-1\)也为卡迈尔数,所以这样的数是无穷的。所以单单费马小定理是不足以判断素数的,故引入新的引理
二次探测定理
对于\(p\)是素数,若有$$x^2≡1(mod~p)$$ $$(x+1)(x-1)≡0(mod~p)$$ $$p|(x+1),p|(x-1)$$
则小于\(p\)的解只有$$x_1=1,x_2=p-1$$ 代入验证即可
那么再根据费马小定理将\(a^{p-1}\)看做\(x^2\)那么也必须满足上述式子。如果不满足那说明一定不是素数,如果将\(a^{p-1}\)看做\(x^2\)成立,那么再将\(a^{\frac{n-1}{2}}\)看做\(x^2\)看是否满足式子,直到\(p-1\)除以\(2\)除到奇数,这期间要么全是\(1\),要么出现\(p-1\)后然后全是\(1\),\(p-1\)不能出现在最后不然连费马小定理也不满足了
算法
假设\(n\)是一个质数,那么\(n-1\)是一个偶数,我们把\(n-1\)表示成\(d×2^s\)的形式,\(s\)和\(d\)都是正整数且\(d\)是奇数,对任意在正整数内的\(a\)和\(0≤r≤s-1\),必满足下列一个式子
\[a^d ≡ 1(mod~n)
\]
\[a^{2^rd}≡n-1(mod~n)
\]
\(tips\):\(Jim Sinclair\)发现了一组数:\(2, 325, 9375, 28178, 450775, 9780504, 1795265022\)。用它们做\(a\),在\(long long\)范围内不会出错
于是代码
#include <cstdio>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
int prime[8] = {2,3,5,7,13,29,37,89};//用来测试
LL quickpower(LL a,LL b,LL p)
{
LL res = 1;
while(b)
{
if(b & 1)
res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res % p;
}
bool miller_rabin(int a,int n)
{
int d = n - 1,r = 0;
while(!(d & 1))
{
d >>= 1;
r ++;
}
LL x = quickpower(a,d,n);
if(x == 1) return true;
for(int i=0;i<r;i++)
{
if(x == n-1) return true;
x = x * x % n;
}
return false;
}
//总之,若 n 是素数,则 a^d = 1(mod n)
//或 存在0 ≤i < r使(a ^ (d * 2^i)) = n-1(mod n)
bool is_prime(int n)
{
if(n <= 1) return false;
for(int i=0;i<=7;i++)
if(n == prime[i]) return true;
for(int i=0;i<=7;i++)
if(!miller_rabin(prime[i],n)) return false;
return true;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
if(is_prime(n))
{
printf("%d是素数\n",n);
continue;
}
else printf("%d不是素数\n",n);
}
return 0;
}

浙公网安备 33010602011771号