GCD & LCM Inverse POJ - 2429 Pollard_rho大数因子分解

题目链接


Given two positive integers a and b, we can easily calculate the greatest common divisor (GCD) and the least common multiple (LCM) of a and b. But what about the inverse? That is: given GCD and LCM, finding a and b. 

Input

The input contains multiple test cases, each of which contains two positive integers, the GCD and the LCM. You can assume that these two numbers are both less than 2^63. 

Output

For each test case, output a and b in ascending order. If there are multiple solutions, output the pair with smallest a + b. 

Sample Input

3 60

Sample Output

12 15

题意:

给你两个数的gcd 和lcm,让你求这两个数.

思路:

Pollard_rho大数因子分解;
ollard_rho算法的大致流程是 先判断当前数是否是素数(Miller_rabin)了,如果是则直接返回。如果不是素数的话,试图找到当前数的一个因子(可以不是质因子)。然后递归对该因子和约去这个因子的另一个因子进行分解。

那么自然的疑问就是,怎么找到当前数n的一个因子?当然不是一个一个慢慢试验,而是一种神奇的想法。其实这个找因子的过程我理解的不是非常透彻,感觉还是有一点儿试的意味,但不是盲目的枚举,而是一种随机化算法。我们假设要找的因子为p,他是随机取一个x1,由x1构造x2,使得{p可以整除x1-x2 && x1-x2不能整除n}则p=gcd(x1-x2,n),结果可能是1也可能不是1。如果不是1就找寻成功了一个因子,返回因子;如果是1就寻找失败,那么我们就要不断调整x2,具体的办法通常是x2=x2*x2+c(c是自己定的)直到出现x2出现了循环==x1了表示x1选取失败重新选取x1重复上述过程.

我们找出这个数的因子后,然后dfs寻找最优解

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long LL;
int pri[]={2,3,5,7,11,13,17,19,23,29,31};
const LL INF = 1000000000000000000LL;

LL mult(LL a, LL b, LL mod){
    LL ans = 0;
    a %= mod;
    while(b){
        if(b&1){
            ans += a;
            if(ans >= mod) ans -=mod;
        }b >>= 1;
        a <<= 1;
        if (a >= mod) a-= mod;
    }return ans;
}

LL qpow(LL x, LL n, LL mod){
    LL ans = 1;
    while(n){
        if (n&1) ans = mult(ans ,x ,mod);
        n >>= 1;
        x = mult(x, x, mod);
    }return ans;
}

LL gcd(LL a, LL b){
    return b ? gcd(b, a % b) : a;
}

bool witness(LL n, LL c){
    LL s = qpow(c, n-1, n);
    if(s != 1) return false;
    LL p = n - 1;
    while(!(p&1) && s == 1){
        p >>= 1;
        s = qpow(c, p, n);
    }if(s == 1 || s == n-1) return true;
    return false;
}

bool miller_rabin(LL n){
    if(n < 32){
        for(int i = 0; i < 11; ++i)
            if(pri[i] == n) return true;
        return false;
    }for(int  i = 1; i < 10; ++i)
        if(!witness(n, pri[i])) return false;//枚举素数进行判断.
    return true;
}

LL pollard_rho(LL n, LL c){
    LL x = rand() % n, y = x, i = 1, k = 2, d;
    while(1){
        i++;
        x = (mult(x, x, n) + c) % n;
        d =gcd( y - x + n, n);
        if(d > 1 && d < n) return d;
        if(y == x) return n;
        if(i == k){
            k <<= 1;
            y = x;
        }
    }
}

LL fac[3000];
int cnt;
void find_factor(LL n){
    if(miller_rabin(n)){
        fac[++cnt] = n;//储存每一个因子
        return ;
    }
    LL p = n;
    while(p >= n) p = pollard_rho(n,rand() % (n - 1) + 1);
    find_factor(p);
    find_factor(n / p);
}

LL ans1,ans2, nn, f[3000];
void dfs(LL sum, int step){
    if(step == cnt + 1){
        if(sum + nn / sum < ans1){//寻找和最小的最优解
            ans1 = sum + nn / sum;
            ans2 = sum;
        }return ;
    }dfs(sum *(LL)f[step], step + 1);
    dfs(sum, step + 1);
}

int main(){
    LL a, b;
    while(scanf("%lld %lld", &a, &b) != EOF){
        if (a == b){
            printf("%lld %lld\n",a, b);
            continue;
        }
        b /= a;//这样处理后只需要寻找b的因子然后答案乘以a就可以了
        nn = b;
        cnt = 0;
        find_factor(b);
        sort(fac + 1, fac + 1 + cnt);
        int t = cnt;
        cnt = 0;
        for(int  i = 1; i <= t; ++i)
            if(fac[i] != fac[i - 1])
                f[++cnt] = fac[i];
            else f[cnt] *= fac[i];
            ans1 = INF;
            dfs(1, 1);
            LL w = nn / ans2;
            if(w > ans2) swap(w, ans2);
            printf("%lld %lld\n",a * w, a * ans2);
    }return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值