看了好久终于把这个Miller_Rabin搞懂了,觉得自己棒棒哒~~~最后是在下面那篇博客里搞懂的,这里推荐给大家
-->参考<--
【写在前面】
费马定理 and 二次探测<证明来源>
然后费马定理是一个必要条件,也就是说素数一定满足这个定理,但满足这个定理的不一定是素数,比如说Carmichael数(我没研究过,有兴趣的同学自己百度吧,反正这种数就是反例)。
【正文】
了解这两个东西后,我们就可以开始搞事情了
问题引入
这道题就是让我们在给出的整数集合中统计有多少个素数,这个整数集合的大小在int范围以内,大概就是4294967296(哈哈,其实就是1e9级别的)
幼儿园做法:对于集合中的每一个数,我们都用试除法,每个数都会有次的枚举,大概就是一个数65536上线,然后还有好多好多的数,妥妥的TLE
小学生做法:机智一点的小可爱会想到什么线性筛,什么埃拉托斯特尼筛法,但是哪一个的复杂度里没有带 n 的呢????又是一个TLE
高中生做法:胡乱的来几下Miller_Rabin,没错就是宇宙无敌超可爱的博主我啦(请自动无视)
通过这几种方法的对比,我们就知道了Miller_Rabin算法的作用,对于很大的一个数快速判断其是否为质数。
但我们要知道Miller_Rabin这个算法是个大概率算法,也就是说不能100%保证验证的正确性,但据说75%的概率准确,而且通常情况下多搞几次,错的概率就很小了
铺垫弄完了,我们正经的讲算法了!!!
假设现在我们需要判断一个数x是否为质数:
若为2,则判断true
若为非2的偶数或1,则直接判断false
若都不是的话,我们就前往下一步
由于费马定理:a^(p-1)≡1(mod p).【p为质数】那如果我们的目标 x 不满足:a^(x-1)≡1(mod x)则x肯定不为质数
然后令 x-1 = u* 2^t ,求出 u,t,其中u是奇数(因为如果u为偶数,那么u中肯定含有因子2,那就可以提到2^t里面去)
变一下形:a^(x-1)≡1(mod x) => a^(u* 2^t )≡1(mod x) => (a^u)^(2^t )≡1(mod x) 【对于a我们是随机取的一个1~x之间的数】
我们令b=(a^u)%x,然后是t次循环,每次循环都让y=(b*b)%x,b=y,这样t次循环之后b=a^(u*2^t)=a^(n-1)了。这个时候二次探测就派上用场了,由于y=(b*b)%x中b是1~x中间的一个数(因为b一直在对x取模),若 y=(b*b)%x = 1,假设x是质数,那么b=1或b=x-1,那反过来如果b!=1且b!=x-1,那x肯定不是素数了
t次循环结束后,由于费马定理,这时如果b!=1,那x就不为素数了
因为Miller-Rabin得到的结果的正确率为 75%,所以要多次循环来提高正确率
最后若循环多次之后还没返回false,那么x肯定是素数了,返回true
【代码】
#include
#include
#include
#include
#include
#define ll long long
#define in read()
using namespace std;
ll ans=0,n,x;
inline ll read(){
char ch;ll res=0;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
ll mod_mul(ll a,ll b,ll mod){//快速积(和快速幂思想一模一样)
ll ans=0;
while(b){
if(b&1) ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
ll mod_pow(ll a,ll b,ll mod){//快速幂
ll ans=1;
while(b){
if(b&1) ans=mod_mul(ans,a,mod);//这个地方要注意一下在这里可以写作ans*a%mod,但更一般的情况下,比如测的数很大很大,此时乘的时候可能会爆long long,而用函数的时候就边乘边取模,就不会爆了
a=mod_mul(a,a,mod);
b>>=1;
}
return ans;
}
bool miller(ll x){//由于和讲解时使用的符号都是统一的,所以我就不再批注了
int i,j,k,s=10;//s是循环的次数,用来提高正确率的那个
ll u=x-1,t=0;
if(x==2) return true;
if(!(x&1)||x<2) return false;
while(!(u&1)){//如果为偶数,就一直除以2,直到u为奇数
t++;
u>>=1;
}
for(i=1;i<=s;++i){
ll a=rand()%(x-1)+1;//随机搞,不过由可爱的勾勾同学亲自试验,发现如果用素数搞会快哦
ll b=mod_pow(a,u,x),y;
for(j=0;j
对于评论区我进行解释:
由于这道题我是作为Miller_Rabin的板子题,就把它当做最一般的情况进行处理的,并没有太考虑数据范围
所以有些地方针对此题是可以简写的,甚至用幼儿园做法