poj 1811

Description

Given a big integer number, you are required to find out whether it's a prime number.

Input

The first line contains the number of test cases T (1 <= T <= 20 ), then the following T lines each contains an integer number N (2 <= N < 2 54).

Output

For each test case, if N is a prime number, output a line containing the word "Prime", otherwise, output a line containing the smallest prime factor of N.

Sample Input

2
5
10

Sample Output

Prime
2
-------------------------------------------------------
http://xingyezhi.diandian.com/post/2012-02-26/15529645

Millier-Rabin

根据费马小定理,如果p是素数,则a^(p-1)≡1(mod p)对所有的a∈[1,n-1]成立。所以如果在[1,n-1]中随机取出一个a,发现不满足费马小定理,则证明n必为合数。

为了计算a^(n-1)modn,我们把n-1分解为u*2^t的形式,其中t>=1且u是奇数;亦即,n-1的二进制表示是奇数u的二进制表示后面跟上t个零。因此,a^(n-1)≡(a^u)^(2^t)(mod n),所以可以通过先计算a^u mod n,然后对结果连续平方t次来计算a^(n-1) mod n。

其中还用到定理,如果对模n存在1的非平凡平方根,则n是合数。(如果一个数x满足方程x^2≡1 (mod n),但x不等于对模n来说1的两个‘平凡’平方根:1或-1,则x是对模n来说1的非平凡平方根)。

Pollard rho

原理:设n为待分解的大整数,用某种方法生成a和b,计算p=gcd(a-b,n),直到p不为1或a,b出现循环时为止,若p=n,则说明n是一个素数,否则p为n的一个约数。

算法步骤:选取一个小的随机数x1,迭代生成x[i] = x[i-1]^2+c,一般去c=1,若序列出现循环则退出,计算p=gcd(x[i-1]-x[i],n),若p=1则返回上一步继续迭代,否则跳出迭代过程。若p=n,则n为素数,否则p为n的一个约数,并递归分解p和n/p。

可以在θ(sqrt(p))的期望时间内找到n的一个小因子p。但对于因子很少,因子值很大的大整数n,该方法依然不是很有效。

POJ-1811

#include<iostream> #include<ctime> #include<cstdlib> #include<cmath> #include<algorithm> #define MAX (pow(2.0, 60))                               //标记最大值 #define C 240 #define TIME 12                                                 //Miller测试次数 using namespace std;

__int64 MIN;

__int64 gcd(__int64 a, __int64 b) //计算a和b的最大公约数 {    if (b == 0)        return a;    return gcd(b, a % b); }

__int64 mod_mult(__int64 a, __int64 b, __int64 n) //计算(a*b) mod n {    __int64 s = 0;    a = a % n;    while (b) {        if (b & 1) {            s += a;            if (s >= n)                s -= n;        }        a = a << 1;        if (a >= n)            a -= n;        b = b >> 1;    }

   return s; }

__int64 mod_exp(__int64 a, __int64 b, __int64 n) //计算(a^b) mod n {    __int64 d = 1;    a = a % n;    while (b >= 1) {        if (b & 1)            d = mod_mult(d, a, n);        a = mod_mult(a, a, n);        b = b >> 1;    }    return d; }

bool Wintess(__int64 a, __int64 n) //以a为基对n进行Miller测试并实现二次探测 {    __int64 m, x, y;    int i, j = 0;    m = n - 1;    while (m % 2 == 0) //计算(n-1)=m*(2^j)中的j和m,j=0时m=n-1,不断的除以2直至n为奇数    {        m = m >> 1;        j++;    }    x = mod_exp(a, m, n);    for (i = 1; i <= j; i++) {        y = mod_exp(x, 2, n);        if ((y == 1) && (x != 1) && (x != n - 1)) //二次探测            return true; //返回true时,n是合数

       x = y;    }    if (y != 1)        return true;    return false; }

bool miller_rabin(int times, __int64 n) //对n进行s次的Miller测试 {    __int64 a;    int i;    if (n == 1)        return false;    if (n == 2)        return true;    if (n % 2 == 0)        return false;    srand(time(NULL));    for (i = 1; i <= times; i++) {        a = rand() % (n - 1) + 1;        if (Wintess(a, n))            return false;    }    return true; }

__int64 Pollard(__int64 n, int c) //对n进行因字分解,找出n的一个因子,注意该因子不一定是最小的 {    __int64 i, k, x, y, d;    srand(time(NULL));    i = 1;    k = 2;    x = rand() % n;    y = x;    while (true) {        i++;        x = (mod_mult(x, x, n) + c) % n;        d = gcd(y - x, n);        if (d > 1 && d < n)            return d;        if (y == x) //该数已经出现过,直接返回即可            return n;        if (i == k) {            y = x;            k = k << 1;        }    } }

void get_small(__int64 n, int c) //找出最小的素数因子 {    __int64 m;    if (n == 1)        return;    if (miller_rabin(TIME, n)) //判断是否为素数    {        if (n < MIN)            MIN = n;        return;    }    m = n;    while (m == n) //找出n的一个因子        m = Pollard(n, c--);    get_small(m, c); //二分查找    get_small(n / m, c); }

int main() {    int total;    __int64 n;    scanf("%d", &total);    while (total--) {        scanf("%I64d", &n);        MIN = MAX;        if (miller_rabin(TIME, n))            printf("Prime\n");        else {            get_small(n, C);            printf("%I64d\n", MIN);        }    }    return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值