分解質因數

對一個整數進行分解質因數。
方法一:
暴力:

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閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容

  • 題目將一個正整數分解質因數。例如:輸入90,打印出90=233*5. 程序分析 對n進行分解質因數,應先找到一個最...
    NoFacePeace閱讀 770評論 0 0
  • 題目: 將一個正整數分解質因數。例如:輸入90,打印出90=233*5。 程序分析: 對n進行分解質因數,應先找到...
    Hughman閱讀 1,580評論 0 3
  • 題目內容:每個非素數(合數)都可以寫成幾個素數(也可稱為質數)相乘的形式,這幾個素數就都叫做這個合數的質因數。比如...
    Jesse1995閱讀 1,149評論 0 0
  • //輸入兩個數字a,b,則輸出從a到b之間的所有整數的分解出質因數乘積的式子 void calArray(int ...
    Tangbh閱讀 482評論 0 0
  • 今天無意中在書架上翻開了一個只用了一半的讀書筆記本,發現在2016年11月3日自己記下的思維導圖——《知道做到》,...
    武曉明閱讀 1,595評論 4 12