Miller_rabin 测试素数

本文介绍了一种基于费马小定理的素数检测方法,并详细解释了如何通过加强该定理来提高检测准确率,最终引入了Miller-Rabin素性测试算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用途:快速判断一个数是否是素数

费马小定理:对于素数p与任意与p互素的整数a满足a(p-1) ≡1(mod p)

若整数p为素数,必然满足费马小定理,故随机选取一些a快速幂判断是否成立即可,若费马小定理公式成立则认为p有极大概率为素数。

一些数具有巧妙地性质,对于一些特殊的a满足费马小定理公式,却不是素数,比如伪素数341=11*31,但用a=2不能正确探测,更有一些绝对伪素数如561=3*11*17,无论用什么样的a,只要a和p互素都无法成功。故引用确定性的二次探测算法。

对于a在满足费马小定理公式的前提下,不断运用引理进行二次探测。即:若a(p-1) ≡1(mod p),若(p-1)为偶数,则应有(a (p-1)/2)2 ≡ ± 1(mod p),若为+1,继续看(p-1)/2是否为偶数以此类推。
这就是Fermat素性测试。
人们自然会想,如果考虑了所有小于n的底数a,出错的概率是否就可以降到0呢?没想到的是,居然就有这样的合数,它可以通过所有a的测试。Carmichael第一个发现这样极端的伪素数,他把它们称作Carmichael数。你一定会以为这样的数一定很大。错。第一个Carmichael数小得惊人,仅仅是一个三位数,561。前10亿个自然数中Carmichael数也有600个之多。Carmichael数的存在说明,我们还需要继续加强素性判断的算法。

Miller和Rabin两个人的工作让Fermat素性测试迈出了革命性的一步,建立了传说中的Miller-Rabin素性测试算法。新的测试基于下面的定理:

引理:若p为奇素数,方程x^2≡1(mod p)的解只有±1

如果p是素数,x是小于p的正整数,且x^2 mod p = 1,
那么要么x=1,要么x=p-1。
这是显然的,因为x^2 mod p = 1相当于p能整除x^2-1,
即也p能整除(x+1)(x-1)。
由于p是素数,那么只可能是x-1能被p整除(此时x=1)或x+1能被p整除(此时x=p-1)。

Miller-Rabin素性测试的方法:
不断地提取指数n-1中的因子2,把n-1表示成d*2^r(其中d是一个奇数)。那么我们需要计算的东西就变成了a的d*2^r次方除以n的余数。于是,a^(d * 2^(r-1))要么等于1,要么等于n-1。如果a^(d * 2^(r-1))等于1,定理继续适用于a^(d * 2^(r-2)),这样不断开方开下去,直到对于某个i满足a^(d * 2^i) mod n = n-1或者最后指数中的2用完了得到的a^d mod n=1或n-1。这样,Fermat小定理加强为如下形式:
尽可能提取因子2,把n-1表示成d*2^r,如果n是一个素数,那么或者a^d mod n=1,或者存在某个i使得a^(d*2^i) mod n=n-1 ( 0<=i

代码

// by spli
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#define LL long long
using namespace std;

const int t=20;
int x;
int p,q;
map<LL,LL,int>m;

LL random(LL a){//随机一个数
    return ((double)rand()/RAND_MAX*a+0.5);
}

LL exp(LL a,LL b,LL p){//快速幂
    LL res=1;
    while(b){
        if(b&1) (res*=a)%=p;
        b>>=1;
        (a*=a)%=p;
    }
    return res;
}

LL mul(LL a,LL b,LL p){//快速乘法
    LL res=0;
    while(b){
        if(b&1) (res+=a)%=p,b--;//
        b>>=1;
        (a+=a)%=p;
    }
    return res;
}

bool judge(LL a,LL n){
    LL tmp=n-1;
    int j=0;
    while(tmp%2==0){
        tmp/=2;
        j++;
    }
    LL b=exp(a,tmp,n);
    if(b==1||b==n-1) return 1;
    while(j--){
        b=mul(b,b,n);
        if(b==n-1) return 1;
    }
    return 0;
}

bool miller_rabin(LL n){
    if(n==2) return 1;
    if(n<2||n%2==0) return 0;
    for(int i=1;i<=t;++i){
        LL a=random(n-2)+1;
        if(!judge(a,n)) return 0;
    }
    return 1;
}

int main(){
    scanf("%d%d",&p,&q);
    while(q--){
        scanf("%d",&x);
        if(miller_rabin(x))
            cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值