分解质因数

对一个整数进行分解质因数。
方法一:
暴力:

long long fact[100];
int tot;
void find(long long n)
{
    long long len=ceil(sqrt(n+0.0));
    for(long long i=2;i<=len;i++)
    {
        if(n%i==0)
        {
            fact[++tot]=i;
            n/=i;
            while(n%i==0)
            {
                n/=i;
            }
        }
    }
}

方法二:
Pollard Rho算法
时间复杂度为n^0.25

原文请点击这里
Pollard Rho算法分解一个数n的过程大体上是这样子的:
1、找到一个数p,使得p|n,将n分解为p与n/p
2、如果p或n/p不为质数,将其带入递归上述过程
3、如果其是质数,将其记录并退出
是不是很sb。。。有人就会问了:这跟暴力分解有什么区别?好像时间复杂度还比暴力高一些。。。
所以:下面的优化才是关键。
第一个优化,使用Miller Rabin算法判定其是否为质数,这个不多提。
关键就在于接下来的这个优化。对于一个大整数n,我们要找到一个p满足p|n,这显然如大海捞针。但是如果我们要找出p1、p2,使得abs(p1−p2)|n,这看起来似乎要容易一些。实际上我们只需要找出gcd(abs(p1−p2),n)>1的p1、p2,则其gcd值肯定为n的约数。这看起来又容易了一些。
实际上,不止容易一些,而是容易许多。根据某个玄学理论(生日悖论,详见百度,在此不赘述),这种两两比较的方式,在加入比较的数越来越多的时候,其概率会大大提升,比找一个数的概率提升快很多。
于是现在,找p的过程变成了这个样子:
1、找到一个数p1
2、通过某种玄学推导手段找出一个与p1对应的p2
3、判断gcd(abs(p1−p2),n)是否为n的因子(1和n本身除外),如果不是则将p2作为新的p1,重复过程,否则就找到了n的因子
怎么又是玄学?因为只有通过推导手段,才能保证不做重复判断,浪费时间。理论上的推导手段可以有很多,但实际使用中统一使用如下公式推导:

p2=(p1^2+c) mod n

其中c为随机常数。
这个公式的好处:
1、显然推导出来的p2-p1差值基本不会相等。
2、可以证明,该推导结果会出现循环。也就是说,在出现循环之前,结果不会重复,少做了许多无用的判断。
出现循环了怎么办?换一个随机常数再搞。这就是该算法“非完美”的地方,如果人品太差那就。。。不过根据上面函数图像可知,两个随机常数产生的推导结果基本不会有重复,所以就可以放心开搞了。
最后一点,判环怎么判?floyd判圈算法搞定。(一个标记以另一个标记几倍速度走,在环上总能碰到。详见百度)
需要注意的是,之所以不能一个标记定在原地,是因为循环节不一定在开头就产生,可能走着走着才遇到循环。这条路径就类似于ρ,Pollard Rho算法因此得名。

Prime Test
题意:
判定一个数是否素数,如果不是,输出它的最小质因数

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
//  计算(a * b) % mod
long long multiMod(long long a,long long b,long long mod)
{
    long long res=0;
    while(b)
    {
        if(b&1)
        {
            res=res+a;
            if(res>=mod) res-=mod;
        }
        a=a+a;
        if(a>=mod) a-=mod;
        b>>=1;
    }
    return res;
}
//  计算 (a ^ b) % mod
long long powMod(long long a,long long b,long long mod)
{
    long long res=1;
    while(b)
    {
        if(b&1)
        {
            res=multiMod(res,a,mod);
        }
        a=multiMod(a,a,mod);
        b>>=1;
    }
    return res;
}
//  通过a ^ (n - 1) = 1(mod n)来判断n是不是素数
//  n - 1 = x * 2 ^ t中间使用二次判断
//  是合数返回true,不一定是合数返回false
bool check(long long a,long long n,long long x,int t)
{
    long long res=powMod(a,x,n);
    long long last=res;
    for(int i=1;i<=t;i++)
    {
        res=multiMod(res,res,n);
        if(res==1&&last!=1&&last!=n-1) return true;//合数
        last=res;
    }
    return res!=1;//最后res=(a^(n-1) % n),如果res!=1,那么不满足费小马定理,说明不是素数
}
//  生成[ 0 , n ]的随机数
long long randomVal(long long n)
{
    //rand(): 0~RAND_MAX;
    return ((double)rand()/RAND_MAX*n+0.5);
}
//  随机算法判定次数,一般8~10次就够了
const int times=8;
//  Miller_Rabin算法
//  是素数返回true,(可能是伪素数)
//  不是素数返回false
bool Miller_Rabin(long long n)
{
    if(n<2) return false;
    if(n==2) return true;
    if(!(n&1)) return false;//  偶数(合数)
    long long x=n-1,t=0;
    while((x&1)==0)
    {
        t++;
        x>>=1;
    }
    for(int i=0;i<times;i++)
    {
        long long a=randomVal(n-2)+1;
        if(check(a,n,x,t)) return false;
    }
    return true;
}
long long gcd(long long a,long long b)
{
    return b==0?a:gcd(b,a%b);
}
long long Pollard_Rho(long long n,long long c)//  找出n的一个因子
{
    int i=1,j=2;
    long long x=((double)rand()/RAND_MAX*(n-2)+0.5)+1,y=x;//随机初始化一个基数
    while(true)
    {
        i++;
        x=(multiMod(x,x,n)+c)%n;//玄学递推
        long long val=gcd((y-x+n)%n,n);
        if(val!=1&&val!=n) return val;
        if(y==x) return FAIL;//y为x的备份,相等则说明遇到圈,退出
        if(i==j)
        {
            y=x;
            j<<=1;
        }//更新y,判圈算法应用
    }
}
long long fact[100];//  质因数分解结果(结果是无序的)
int tot; //  质因数的个数
const int FAIL=-1;
void find(long long n,int c)//  对n进行质因子分解
{
    if(n==1) return ;
    if(Miller_Rabin(n))
    {
        fact[++tot]=n;
        return;
    }
    long long x=FAIL;
    int k=c;
    while(x==FAIL)
    {
        x=Pollard_Rho(n,k--);
    }
    find(n/x,c);
    find(x,c);
}
const int INF=1e18;
int main()
{
    int t;
    scanf("%d",&t);
    long long n,ans;
    while(t--)
    {
        scanf("%lld",&n);
        tot=0;
        if(Miller_Rabin(n)) printf("Prime\n");
        else
        {
            find(n,120);
            ans=INF;
            for(int i=1;i<=tot;i++)
            {
                ans=min(ans,fact[i]);
            }
            printf("%lld\n",ans);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 题目将一个正整数分解质因数。例如:输入90,打印出90=233*5. 程序分析 对n进行分解质因数,应先找到一个最...
    NoFacePeace阅读 750评论 0 0
  • 题目: 将一个正整数分解质因数。例如:输入90,打印出90=233*5。 程序分析: 对n进行分解质因数,应先找到...
    Hughman阅读 1,477评论 0 3
  • 题目内容:每个非素数(合数)都可以写成几个素数(也可称为质数)相乘的形式,这几个素数就都叫做这个合数的质因数。比如...
    Jesse1995阅读 1,112评论 0 0
  • //输入两个数字a,b,则输出从a到b之间的所有整数的分解出质因数乘积的式子 void calArray(int ...
    Tangbh阅读 460评论 0 0
  • 今天无意中在书架上翻开了一个只用了一半的读书笔记本,发现在2016年11月3日自己记下的思维导图——《知道做到》,...
    武晓明阅读 1,513评论 4 12