POJ1811-Prime Test-素数测试+Pollard rho因数分解

题意:

给你一个数n(n <= 2^54),判断n是不是素数,如果是输出Prime,否则输出n最小的素因子


参考:先用miller_rubin素数测试判断是否为素数,是则用Pollard rho因数分解得到最小素因子

以下代码是网上找的,前面的稳定在1S+,后面的1/3概率TLE,但是run time 稳定在500S

稳定1S+代码:

#include <cstdio>
#include <cstdlib>
#include <ctime>
typedef __int64 LL;
int T;
LL n, s;
LL factor[110000];
LL mods(LL x, LL y, LL n){
	x %= n;
	y %= n;
	LL tmp = 0;
	while(y){
		if(y & 1)
			tmp = (tmp + x) % n;
		x = (x << 1) % n;
		y >>= 1;
	}
	return tmp;
}
LL pow(LL x, LL y, LL n)
{
	x %= n;
	LL tmp = 1;
	while(y){
		if(y&1) tmp = mods(tmp, x, n);
		x = mods(x, x, n);
		y >>= 1;
	}
	return tmp;
}
int judge(LL tmp, LL n, LL m, LL t){//n - 1 == m * 2^ t
	LL tp = m;
	LL v = pow(tmp , m, n);
	LL last = v;
	for(LL i = 1; i <= t; i++){
		v = mods(v, v, n);
		if(v == 1){
			if(last != 1 && last != n-1)
				return 0;
		}
		last = v;
		tp <<= 1;
	}
	if(v == 1)return 1;
	return 0;
}
int miller_rubin(LL x, int k){
	LL cnt = 0;
	LL m = x - 1;
	while(!(m & 1)) cnt++, m >>= 1;
	while(k--){
		LL tmp = rand()%(x-1) + 1;
		if(!judge(tmp, x, m, cnt)){
			return 0;
		}
	}
	return 1;
}
LL gcd(LL a, LL b){
	return b == 0?a: gcd(b, a%b);
}
LL f(LL x, LL n, LL c){
	return (mods(x, x, n) + c) % n;
}
LL poll(LL n, LL c){
	if(!(n & 1))return 2;
	LL x = rand() % n;
	LL y = x;
	LL i = 1;
	LL k = 2;
	while(1){
		i++;
		x = f(x, n, c);
		LL d = gcd(y - x + n, n);
		if(d != 1 && d != n)
			return d;
		if(y == x)return n;
		if(i == k){
			y = x;
			k += k;
		}
	}
}
void find(LL n){
	if(miller_rubin(n,5)){
		factor[s++] = n;
		return;
	}
	LL p = n;
	while(p >= n) p = poll(p, (LL)(rand()%(n-1)+1));
	find(p);
	find(n/p);
}
int main(){
	srand(time(NULL));
	scanf("%d",&T);
	while(T--){
		scanf("%I64d",&n);
		if(n == 1)break;
		s = 0;
		find(n);
		if(s == 1){
			printf("Prime\n");
			continue;
		}
		LL tp = 10000000000LL;
		for(int i = 0; i < s; i++){
			if(tp > factor[i])
				tp = factor[i];
		}
		printf("%I64d\n",tp);	
	}
	return 0;
}


大几率TLE,但是ac time 500ms+


//此代码提交有1/3几率TLE,但是AC时间稳定在500S-
#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、付费专栏及课程。

余额充值