POJ 3641 Pseudoprime numbers 米勒拉宾素数判定+埃氏筛法

一、思路

对于输入的一个数字n和a,我们用快速幂判断 n ^ a % n 是否等于n,如果不等于直接输出no,等于的话,再判断n是否为素数,如果n为素数,输出no,否则输出yes。

判断素数的话,对于30000以下的,我采用埃氏筛法打表,对于3000以上的,我用米勒拉宾判断。

米勒拉宾算法,就是求出一组r和d,使得 d * 2 * r = n-1,产生小于n大于0的随机数m,判断m ^ (n - 1) == 1 (mod n)是否成立,成立的话,再定义i从0到r-1循环,判断每一个 (m ^ d)^(2^i)是否为1或者p-1,如果是的话,直接判断过,如果都不是,那么n不是素数。

二、代码

#include 
#include 
using namespace std;
typedef unsigned long long ll;
ll r, d;
int primeLimit = 30000;
bool isPrime[30007];
void sieve()
{
    for (int i = 0; i <= primeLimit; i++)
    {
        isPrime[i] = true;
    }
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 1; i * i <= primeLimit; i++)
    {
        if (!isPrime[i])
        {
            continue;
        }
        for (int j = 2 * i; j <= primeLimit; j += i)
        {
            isPrime[j] = false;
        }
    }
}
void getRd(ll p)
{
    d = p - 1;
    r = 0;
    while (!(d & 1))
    {
        d = d >> 1;
        r++;
    }
}
ll getLongRand(ll p)
{
    int x = 0;
    while (x == 0)
    {
        x = rand();
        if (x < 0)
        {
            x = x * (-1);
        }
        if (x >= p)
        {
            x = x % p;
        }
    }
    return (ll)x;
}
ll mulMod(ll a, ll b, ll mod)
{
    ll res = 0;
    while (b)
    {
        if (b & 1)
        {
            res = (res + a) % mod;
        }
        a = (a << 1) % mod;
        b = b >> 1;
    }
    return res;
}
ll powMod(ll a, ll b, ll mod)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = mulMod(res, a, mod);
        }
        a = mulMod(a, a, mod);
        b = b >> 1;
    }
    return res;
}
bool millerRabin(ll p)
{
    ll a = getLongRand(p);
    if (powMod(a, p - 1, p) != 1)
    {
        return false;
    }
    ll k = powMod(a, d, p);
    for (int i = 0; i < r; i++)
    {
        ll parameter = powMod(k, powMod(2, i, p), p);
        if (parameter == 1 || parameter == p - 1)
        {
            return true;
        }
    }
    return false;
}
bool multipleMillerRabin(ll p)
{
    getRd(p);
    for (int i = 0; i < 20; i++)
    {
        if (!millerRabin(p))
        {
            return false;
        }
    }
    return true;
}
bool judgePrime(ll p)
{
    if (p <= primeLimit)
    {
        return isPrime[(int)p];
    }
    else if (!(p & 1))
    {
        return false;
    }
    else
    {
        return multipleMillerRabin(p);
    }
}
bool fermet(ll a, ll p)
{
    return powMod(a, p, p) == (a % p);
}
int main()
{
    sieve();
    ll a, p;
    while (true)
    {
        scanf("%lld%lld", &p, &a);
        if (a == 0 && p == 0)
        {
            break;
        }
        if (fermet(a, p) && !judgePrime(p))
        {
            printf("yes\n");
        }
        else
        {
            printf("no\n");
        }
    }
    return 0;
}

三、我对米勒拉宾算法个人小小理解

// 这里引入费马小定理, 当 p 时一个素数时,对任意的整数a,一定有 pow ( a , p ) % p == a % p
// 当 1 <= a <= p - 1 我们对等式两边同时除以 a,得到 pow ( a , p - 1 ) % p == 1
// 所以只要对于大整数 p 和随机数 a ,如果不满足这个规则,那么p一定不是素数
// 为了提高算法的准确性,我们把 pow ( a , p - 1 ) % p == 1 进行推导,这里用  a ^ (p-1) 代表a的 p - 1 次方
//  a ^ ( p - 1 ) % p == 1
// 定义 r d,使得 ( 2 ^ r ) * d = p - 1
// a ^ ( ( 2 ^ r ) * d ) % p == 1
// ( a ^ ( ( 2 ^ r ) * d ) - 1 ) % p == 0
// 这里可以使用平方差公式进行推导
// (a ^ ( (2 ^ (r - 1) ) * d) - 1) * (a ^ ( (2 ^ (r - 1) ) * d) + 1 ) % p == 0
// (a ^ ( (2 ^ (r - 2) ) * d) - 1) * (a ^ ( (2 ^ (r - 2) ) * d) + 1 ) * (a ^ ( (2 ^ (r - 1) ) * d) + 1) % p == 0
// ....
// (a ^ ( (2 ^ (r - r) ) * d) - 1) * (a ^ ( (2 ^ (r - r) ) * d) + 1) * ...  (a ^ ( (2 ^ (r - 1) ) * d) + 1) % p == 0
// 同时p是素数,如果这些项相乘 % p == 0,那么其中的某一项一定等于0,那么就推出了另外一个判断依据
// 在  0 <= i < r 必定存在 a ^ ( (2 ^ i) * d) 等于 p 或等于 1,否则 p 一定不是素数
// a ^ ( (2 ^ i) * d) = (a ^ d) ^ (2 ^ i)




四、运行情况

 

你可能感兴趣的:(算法)