POJ-1811-Prime Test

182 篇文章 3 订阅
156 篇文章 21 订阅

ACM模版

题目链接

POJ 1811 Prime Test

题解

随机素数测试和大数分解两个核心算法。随机素数测试算法需要进行8~10次测试的样子,可以尽可能的保证结果的正确性。

代码

#include <iostream>

using namespace std;

/*
 *  Miller_Rabin算法进行素数测试
 *  速度快,可以判断一个n(< 2 ^ 63)的数是不是素数
 */

const int S = 8;    //  随机算法判定次数,一般8~10次就够了

//  计算ret = (a * b) % c a, b, c < 2 ^ 63
long long mult_mod(long long a, long long b, long long c)
{
    a %= c;
    b %= c;
    long long ret = 0;
    long long tmp = a;
    while (b)
    {
        if (b & 1)
        {
            ret += tmp;
            if (ret > c)
            {
                ret -= c;   //  直接取模慢很多
            }
        }
        tmp <<= 1;
        if (tmp > c)
        {
            tmp -= c;
        }
        b >>= 1;
    }
    return ret;
}

//  计算ret = (a ^ n) % mod
long long pow_mod(long long a, long long n, long long mod)
{
    long long ret = 1;
    long long temp = a % mod;
    while (n)
    {
        if (n & 1)
        {
            ret = mult_mod(ret, temp, mod);
        }
        temp = mult_mod(temp, temp, mod);
        n >>= 1;
    }
    return ret;
}

//  通过a ^ (n - 1) = 1(mod n)来判断n是不是素数
//  n - 1 = x * 2 ^ t中间使用二次判断
//  是合数返回true,不一定是合数返回false
bool check(long long a, long long n, long long x, long long t)
{
    long long ret = pow_mod(a, x, n);
    long long last = ret;
    for (int i = 1; i <= t; i++)
    {
        ret = mult_mod(ret, ret, n);
        if (ret == 1 && last != 1 && last != n - 1)
        {
            return true;            //  合数
        }
        last = ret;
    }
    if (ret != 1)
    {
        return true;                //  合数
    }
    else
    {
        return false;               //  不一定是合数
    }
}

//  Miller_Rabin算法
//  是素数返回true,(可能是伪素数)
//  不是素数返回false

bool Miller_Rabin(long long n)
{
    if (n < 2)
    {
        return false;
    }
    if (n == 2)
    {
        return true;
    }
    if ((n & 1) == 0)
    {
        return false;   //  偶数(合数)
    }
    long long x = n - 1;
    long long t = 0;
    while ((x & 1) == 0)
    {
        x >>= 1;
        t++;
    }
    srand((int)time(NULL));

    for (int i = 0; i < S; i++)
    {
        long long a = rand() % (n - 1) + 1;
        if (check(a, n, x, t))
        {
            return false;
        }
    }
    return true;
}

/*
 *  pollard_rho算法进行质因数分解
 */
long long factor[100];  //  质因数分解结果(刚返回时是无序的)
int tol = 0;            //  质因数的个数,编号0~tol-1

long long gcd(long a, long b)
{
    long long t;
    while (b)
    {
        t = a;
        a = b;
        b = t % b;
    }
    if (a >= 0)
    {
        return a;
    }
    else
    {
        return -a;
    }
}

//  找出一个因子
long long pollard_rho(long long x, long long c)
{
    long long i = 1;
    long long k = 2;
    srand((int)time(NULL));
    long long x0 = rand() % (x - 1) + 1;
    long long y = x0;
    while (1)
    {
        i++;
        x0 = (mult_mod(x0, x0, x) + c) % x;
        long long d = gcd(y - x0, x);
        if (d != 1 && d != x)
        {
            return d;
        }
        if (y == x0)
        {
            return x;
        }
        if (i == k)
        {
            y = x0;
            k += k;
        }
    }
}

//  对n进行质因子分解,存入factor,k设置为107左右即可
void findfac(long long n, int k)
{
    if (n == 1)
    {
        return ;
    }
    if (Miller_Rabin(n))
    {
        factor[tol++] = n;
        return ;
    }
    long long p = n;
    int c = k;
    while (p >= n)
    {
        p = pollard_rho(p, c--);    //  值变化,防止死循环
    }
    findfac(p, k);
    findfac(n / p, k);
    return ;
}

//  给出一个N(2 ≤ N < 2 ^ 64),如果是素数,输出“Prime”,否则输出最小的质因子
int main(int argc, const char * argv[])
{
    int T;
    long long n;
    cin >> T;
    while (T--)
    {
        cin >> n;
        if (Miller_Rabin(n))
        {
            cout << "Prime\n";
        }
        else
        {
            tol = 0;
            findfac(n, 107);
            long long ans = factor[0];
            for (int i = 1; i < tol; i++)
            {
                ans = min(ans, factor[i]);
            }
            cout << ans << '\n';
        }
    }

    return 0;
}

参考

《素数相关》
《合数相关》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值