Miller-Rabin 素数判定算法

感谢Sunshine_cfbsl的文章:https://blog.csdn.net/sunshine_cfbsl/article/details/52425798

算法核心:

  • 引入随机数进行判断,判断的结果有可能会出错,但是当检验次数增加的时候,错误的概率会降低到一个很低的程度

算法思路:

前提:

(1)费马小定理:对于一个质数p,取任意整数a,满足gcd(p,a)=1,则有

ap11(mod(p))

(2)二次探测定理:对于 0<x<p ,若p是素数,则方程:

x21(mod(p))

的解为:

x1=1 , x2=p1

具体实现:
思路1:
  • 随机多次选取a( a<p ),若

  • ap11(mod(p))

  • 不满足,则p为合数.

    这种方法不够精确,误判的可能性还是有点大,故引入第二种思路。

思路2:
  • 将p-1分解为 2tu,(u{x|x=2k+1})

  • 令b[0]= au mod p

  • b[i]=b[i1]2 ,那么 b[t]=a2tu

  • 构造出b[i]后,根据二次检测法进行判断,即

  • 如果 b[i]==1 , 则有 b[i1]==1 或者 b[i1]==p1

  • 如果不满足上述条件,则p为合数

模板:

const int MAXN = 65;
long long n, x[MAXN];

long long multi(long long a, long long b, long long p) {
    long long ans = 0;
    while(b) {
        if(b&1LL) ans = (ans+a)%p;
        a = (a+a)%p;
        b >>= 1;
    }
    return ans;
}

long long qpow(long long a, long long b, long long p) {
    long long ans = 1;
    while(b) {
        if(b&1LL) ans = multi(ans, a, p);
        a = multi(a, a, p);
        b >>= 1;
    }
    return ans;
}

bool Miller_Rabin(long long n) {
    if(n == 2) return true;
    int s = 20, i, t = 0;
    long long u = n-1;
    while(!(u & 1)) {
        t++;
        u >>= 1;
    }
    while(s--) {
        long long a = rand()%(n-2)+2;
        x[0] = qpow(a, u, n);
        for(i = 1; i <= t; i++) {
            x[i] = multi(x[i-1], x[i-1], n);
            if(x[i] == 1 && x[i-1] != 1 && x[i-1] != n-1) return false;
        }
        if(x[t] != 1) return false;
    }
    return true;
}

相关题目:https://nanti.jisuanke.com/t/25985

题目大意:

给定一个偶数n,求两个质数a、b使得a+b=n

题解:

打个素数表,枚举素数a,用Miller-Rabin判断n-a是否为素数即可

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 65;
unsigned long long n, x[MAXN];

unsigned long long multi(unsigned long long a, unsigned long long b, unsigned long long p) {
    unsigned long long ans = 0;
    while(b) {
        if(b&1LL) ans = (ans+a)%p;
        a = (a+a)%p;
        b >>= 1;
    }
    return ans;
}

unsigned long long qpow(unsigned long long a, unsigned long long b, unsigned long long p) {
    unsigned long long ans = 1;
    while(b) {
        if(b&1LL) ans = multi(ans, a, p);
        a = multi(a, a, p);
        b >>= 1;
    }
    return ans;
}

bool Miller_Rabin(unsigned long long n) {
    if(n == 2) return true;
    int s = 8, i, t = 0;
    unsigned long long u = n-1;
    while(!(u & 1)) {
        t++;
        u >>= 1;
    }
    while(s--) {
        unsigned long long a = rand()%(n-2)+2;
        x[0] = qpow(a, u, n);
        for(i = 1; i <= t; i++) {
            x[i] = multi(x[i-1], x[i-1], n);
            if(x[i] == 1 && x[i-1] != 1 && x[i-1] != n-1) return false;
        }
        if(x[t] != 1) return false;
    }
    return true;
}
bool isprime[1000006];
int prime[1000006];
int cnt=0;
void creat()
{
    memset(isprime,1,sizeof isprime);
    for(int i=2;i<=100000;i++)
    {
        if(isprime[i]) prime[cnt++]=i;
        else continue;
        for(int j=i+i;j<=100000;j+=i)
        {
            isprime[j]=false;
        }
    }
}
int main()
{
    creat();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long n;
        scanf("%lld",&n);
        for(int i=0;i<cnt;i++)
        {
            if(Miller_Rabin(n-prime[i]))
            {
                printf("%lld %lu\n",prime[i],n-prime[i]);
                    break;
            }
        }
    }
    return 0;
}

Ps:如果套用模板,使用long long进行计算会超时,原因不明。将代码中所有的long long 改成unsigned long long之后就能通过了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值