pollard_rho(大数质因子分解)

本文介绍了使用Miller-Rabin素性测试和Pollard's Rho算法进行高效的大数素性判断及因子分解的方法,并通过两个具体题目展示了如何利用这些算法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大佬博客
运用大素数判断和随机化算法来找寻一个因子,而可以递归求出所有的因子。
poj 1811
题意:判断一个数是不是素数,是的话输出prime否则的话输出这个数的最小素因子。

#include<cstdio>
#include<cstring>
#include<ctime>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
int cont;
LL factor[200];//分解结果(返回结果是无序的)
static int S=3;//规定随机多少次,次数越多正确率越高
LL quick_mod_multi(LL a,LL b,LL mod)//快速求a*b%mod
{
    LL t=0;
    a%=mod;
    while(b)
    {
        if(b&1)
        {
            t+=a;
            if(t>=mod)//取模要比减法慢的多
                t-=mod;
        }
        a<<=1;
        if(a>=mod) a-=mod;
        b>>=1;
    }
    return t;
}
LL quick_mod_power(LL a,LL b,LL mod)//快速求a^b%mod
{
    LL res=1;
    a%=mod;
    while(b)
    {
        if(b&1)
            res=quick_mod_multi(res,a,mod);
        b>>=1;
        a=quick_mod_multi(a,a,mod);
    }
    return res;
}
bool miller_rabbin(LL n)//素性检验
{
    if(n==2) return true;
    if(n<2||!(n&1)) return false;
    int t=0;
    LL a,x,y,u=n-1;
    while((u&1)==0) t++,u>>=1;
    for(int i=0; i<S; i++) //随机多少次
    {
        a=rand()%(n-1)+1;
        x=quick_mod_power(a,u,n);
        for(int j=0; j<t; j++)
        {
            y=quick_mod_multi(x,x,n);
            if(y==1&&x!=1&&x!=n-1)
                return false;
            x=y;
        }
        if(x!=1) return false;
    }
    return true;
}
LL get_gcd(LL a,LL b)
{
    if(a==0) return 1;
    if(a<0) return get_gcd(-a,b);
    while(b)
    {
        LL t=a%b;
        a=b;
        b=t;
    }
    return a;
}
LL pollard_rho(LL n,LL c)
{
    LL i=1,k=2,x,y;
    x=rand()%n;
    y=x;
    while(1)
    {
        i++;
        x=(quick_mod_multi(x,x,n)+c)%n;
        LL d=get_gcd(y-x,n);
        if(d>1&&d<n) return d;
        if(y==x) return n;
        if(i==k)
        {
            y=x;
            k<<=1;
        }
    }
}
void findfac(LL n)//找n的素因子
{
    if(n==1) return ;
    if(miller_rabbin(n))
    {
        factor[cont++]=n;
        return ;
    }
    LL p=n;
    while(p>=n)
        p=pollard_rho(p,rand()%(n-1)+1);
    findfac(p);
    findfac(n/p);
}
int main()
{
    srand((unsigned)time(NULL));//poj 上有这个会RE
    int ncase;
    scanf("%d",&ncase);
    while(ncase--)
    {
        LL n;
        scanf("%lld",&n);
        if(miller_rabbin(n)) printf("Prime\n");
        else
        {
            cont=0;
            findfac(n);
            LL ans=factor[0];
            for(int i=0; i<cont; i++)
                ans=min(ans,factor[i]);
            printf("%lld\n",ans);
        }
    }
}

hdu 4344
题意:给出一个长为n(n小于2^63)的管子,现在要在管子上做标记,每隔L个长度单位做一个标记,从管子头端开始,保证最后一次标记恰好在管子的尾端。让你找出有多少个这样的L(n>L),且他们之间两两互素,然后求出这些L的和最大值。
思路:首先可以想到L肯定是n的因子,还要所有互素的因子,只需要找到所有的素因子,统计一下相同的素因子有多少个,假如有k个则不相同的素因子的k次方肯定是互素的,加起来就好了。还要注意只有一个因子的时候这个时候k次幂就是自己本身,所以求的是k-1次幂;

#include<cstdio>
#include<cstring>
#include<ctime>
#include<map>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
int cont;
LL factor[200];//分解结果(返回结果是无序的)
static int S=3;//规定随机多少次,次数越多正确率越高
LL quick_mod_multi(LL a,LL b,LL mod)//快速求a*b%mod
{
    LL t=0;
    a%=mod;
    while(b)
    {
        if(b&1)
        {
            t+=a;
            if(t>=mod)//取模要比减法慢的多
                t-=mod;
        }
        a<<=1;
        if(a>=mod) a-=mod;
        b>>=1;
    }
    return t;
}
LL quick_mod_power(LL a,LL b,LL mod)//快速求a^b%mod
{
    LL res=1;
    a%=mod;
    while(b)
    {
        if(b&1)
            res=quick_mod_multi(res,a,mod);
        b>>=1;
        a=quick_mod_multi(a,a,mod);
    }
    return res;
}
bool miller_rabbin(LL n)//素性检验
{
    if(n==2) return true;
    if(n<2||!(n&1)) return false;
    int t=0;
    LL a,x,y,u=n-1;
    while((u&1)==0) t++,u>>=1;
    for(int i=0; i<S; i++) //随机多少次
    {
        a=rand()%(n-1)+1;
        x=quick_mod_power(a,u,n);
        for(int j=0; j<t; j++)
        {
            y=quick_mod_multi(x,x,n);
            if(y==1&&x!=1&&x!=n-1)
                return false;
            x=y;
        }
        if(x!=1) return false;
    }
    return true;
}
LL get_gcd(LL a,LL b)
{
    if(a==0) return 1;
    if(a<0) return get_gcd(-a,b);
    while(b)
    {
        LL t=a%b;
        a=b;
        b=t;
    }
    return a;
}
LL pollard_rho(LL n,LL c)
{
    LL i=1,k=2,x,y;
    x=rand()%n;
    y=x;
    while(1)
    {
        i++;
        x=(quick_mod_multi(x,x,n)+c)%n;
        LL d=get_gcd(y-x,n);
        if(d>1&&d<n) return d;
        if(y==x) return n;
        if(i==k)
        {
            y=x;
            k<<=1;
        }
    }
}
void findfac(LL n)//找n的素因子
{
    if(n==1) return ;
    if(miller_rabbin(n))
    {
        factor[cont++]=n;
        return ;
    }
    LL p=n;
    while(p>=n)
        p=pollard_rho(p,rand()%(n-1)+1);
    findfac(p);
    findfac(n/p);
}
int main()
{
    srand((unsigned)time(NULL));
    int ncase;
    scanf("%d",&ncase);
    while(ncase--)
    {
        LL n;
        scanf("%lld",&n);
        if(miller_rabbin(n)) printf("Prime\n");
        else
        {
            cont=0;
            findfac(n);
            map<LL,LL> mp;
            for(int i=0; i<cont; i++)
            {
                if(mp[factor[i]]==0)
                    mp[factor[i]]=factor[i];
                else
                    mp[factor[i]]*=factor[i];
            }
            LL ans=0;
            map<LL,LL>::iterator it;
            for(it=mp.begin(); it!=mp.end(); it++)
                ans+=(it->second);
            if(mp.size()==1)
                ans/=factor[0];
            printf("%d %lld\n",mp.size(),ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值