Pollard Rho 方法

对于整数的分解,是指将整数拆成质数乘积的形式。


Birthday Trick :

我们从 [1, 10000] 中随机取出一个数,取到想要的那个数的概率为 110000
对这个问题进行修改,我们从[1, 10000] 中随机取出两个数,取到这两个数的差为我们要的那个数的概率近似为 15000
从选一个数到选两个数得到我们要的数的概率就提高了。
那么我们还可以选择更多的数,来使得我们的到我们要的数的概率大大提高!

引用:
生日悖论
生日悖论,指如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%。从引起逻辑矛盾的角度来说生日悖论并不是一种悖论,从这个数学事实与一般直觉相抵触的意义上,它才称得上是一个悖论。大多数人会认为,23人中有2人生日相同的概率应该远远小于50%。计算与此相关的概率被称为生日问题,在这个问题之后的数学理论已被用于设计著名的密码攻击方法:生日攻击。


由生日悖论转到对整数分解上。
我们选出 x1, x2, x3, x4, x5 …… xn 个数,其中两个的差是 n 的因数或者与 n 的最大公因数大于1。
对于第一种,我们要考虑的是固定的 p 和 q 整除 N,只有两个。
对于第二种,我们要考虑的是组成的数和 N 的最大公约数是否大于 1。
显然第二种情况更能找出因数。

因此我们的方法是在 [2, n-1] 中选取 k 个数,如果 gcd(xi - xj, N) > 1 ,那么我们就找到了相应的因数。


在这个方法中,需要给出一个神奇的函数 f(x)=(x2+a)modN .
这个函数可以找出伪随机数,但是也会出现循环圈。如果出现了循环圈,我们更改一下初始点就好了。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
#define ll long long
using namespace std;
const int S = 8;//随机算法判定次数,一般8~10次就够了  

//计算ret = (a*b)%c  a, b, c < 2^63  
ll mult_mod(ll a, ll b, ll c) {
    a %= c;
    b %= c;
    ll ret = 0;
    ll tem = a;
    while (b) {
        if (b & 1) {
            ret += tem;
            if (ret > c) {
                ret -= c;
            }  
        }
        tem <<= 1;
        if (tem > c) {
            tem -= c;
        }
        b >>= 1;
    }
    return ret;
}

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

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

bool Miller_Rabin(ll n) {
    if (n < 2) {
        return false;
    }
    if (n == 2) {
        return true;
    }
    if ((n & 1) == 0) {
        return false;
    }
    ll x = n - 1;
    ll t = 0;
    while ((x & 1) == 0) {
        x >>= 1;
        t++;
    }

    srand(time(NULL));

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

int tol;//质因数的个数,编号为0~tol-1  
ll factor[100];//质因素分解结果(刚返回时是无序的)  

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

//找出一个因子  
ll pollard_rho(ll x, ll c) {
    ll i = 1, k = 2;
    srand(time(NULL));
    ll x0 = rand() % (x - 1) + 1;//产生随机数x0(并控制其范围在1 ~ x-1之间)  
    ll y = x0;
    while (1) {
        i++;
        x0 = (mult_mod(x0, x0, x) + c) % x;
        ll 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(ll n, int k) {
    if (n == 1) {
        return;
    }
    if (Miller_Rabin(n))//是素数就把这个素因子存起来  
    {
        factor[tol++] = n;
        return;
    }
    int c = k;
    ll p = n;
    while (p >= n) {
        p = pollard_rho(p, c--);
    }
    findfac(p, k);
    findfac(n / p, k);
}

int main() {
    int T;
    ll n;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n);
        if (Miller_Rabin(n)) cout << "Prime" << endl;
        else {
            tol = 0;
            findfac(n, 107);
            ll ans = factor[0];
            for (int i = 1; i < tol; ++i)
                ans = min(ans, factor[i]);
            cout << ans << endl;
        }
    }
    return 0;
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值