Miller_Rabin素数测试

时间:2022-05-20 07:16:01
 #include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; long long mul(long long a,long long n,long long mo){
long long ans=;
while (n){
if (n&) ans=(ans+a)%mo;
a=(a+a)%mo;
n/=;
}
return ans;
} long long pow(long long a,long long n,long long mo){
long long ans=;
while (n){
if (n&) ans=mul(ans,a,mo);
a=mul(a,a,mo);
n/=;
}
return ans;
} bool Miller_Rabin(long long n){
if (n<=){
if (n==) return true;
else return false;
}
if (n%==) return false;
long long u=n-;
while (u%==){
u/=;
}
int t=1e2;
while (t--){
long long a=(rand()%(n-))+;
long long x=pow(a,u,n);
long long w=u*;
long long y=mul(x,x,n);
while (w<n){
if (y==&&x!=&&x!=(n-)) return false;
x=y;
y=mul(y,y,n);
w*=;
}
if (x!=) return false;
}
return true;
} int main(){
int t;
scanf("%d",&t);
while (t--){
long long n;
scanf("%lld",&n);
if (Miller_Rabin(n)) printf("Yes\n");
else printf("No\n");
} }
费马小定理:对于质数p和任意整数a,有a^p ≡ a(mod p)(同余)。反之,若满足a^p ≡ a(mod p),p也有很大概率为质数。
将两边同时约去一个a,则有a^(p-1) ≡ 1(mod p)

也即是说:假设我们要测试n是否为质数。我们可以随机选取一个数a,然后计算a^(n-1) mod n,如果结果不为1,我们可以100%断定n不是质数。

否则我们再随机选取一个新的数a进行测试。如此反复多次,如果每次结果都是1,我们就假定n是质数。

该测试被称为Fermat测试。需要注意的是:Fermat测试不一定是准确的,有可能出现把合数误判为质数的情况。

Miller和Rabin在Fermat测试上,建立了Miller-Rabin质数测试算法。

与Fermat测试相比,增加了一个二次探测定理:

如果p是奇素数,则 x^2 ≡ 1(mod p)的解为 x ≡ 1 或 x ≡ p - 1(mod p)

如果a^(n-1) ≡ 1 (mod n)成立,Miller-Rabin算法不是立即找另一个a进行测试,而是看n-1是不是偶数。如果n-1是偶数,另u=(n-1)/2,并检查是否满足二次探测定理即a^u ≡ 1 或 a^u ≡ n - 1(mod n)。

举个Matrix67 Blog上的例子,假设n=341,我们选取的a=2。则第一次测试时,2^340 mod 341=1。由于340是偶数,因此我们检查2^170,得到2^170 mod 341=1,满足二次探测定理。同时由于170还是偶数,因此我们进一步检查2^85 mod 341=32。此时不满足二次探测定理,因此可以判定341不为质数。