有一類問題,要求我們將一個正整數x,分解為兩個非平凡因子(平凡因子為1與x)的乘積x=ab。
顯然我們需要先檢測x是否為素數(如果是素數將無解),可以使用Miller-Rabin算法來進行測試。
大數分解最簡單的思想也是試除法,就是從2到sqrt(n),一個一個的試驗,直到除到1或者循環完,最后判斷一下是否已經除到1了即可。(當然這是幼稚做法,復雜度是相當高的,不然我就是想打試除法多方便呀)
Pollard Rho原理
生日悖論
如果一年只有365天(不計算閏年),且每個人的生日是任意一天的概率均相等,那么只要隨機選取23人,就有50%以上的概率有兩人同一天生日
解釋:第一個人不會和前面的人重生日(因為前面沒有人),第二個人不重生日的概率為364/365,第三個人363/365……以此類推,那么只要到第23個人,就有,說明這時就有50%以上的概率有兩人同生日
更多的,當一年有n天時,只要人數到達Θ(sqrt(n))的數量級,有至少兩個人同一天生日的概率就可以達到50%以上
圖象表達:
利用生日悖論
利用生日悖論來因數分解,重要的思想就是隨機。
已知我們隨機地從[2,N-1]中選擇出一個數是N的因數的概率是極小的,這也就意味着需要重復隨機選擇來提高正確率。
那么如果我們不是只選擇一個數,而是選擇k個數,保證其中兩個數的差值是N的因數。
而如果其中兩個數x,y的差值為N的因數,就一定會有gcd(|x-y|,N)>1
假設N只有兩個因數(除去自己和1)p和q的情況下,那么就意味着此時只有這兩個數能整除N
但是如果我們要求的是有多少個數x保證gcd(x,N)>1,此時答案就很多了,有p+q-2個
於是我們就得出了一個簡單的策略:
1.在區間[2,N-1]中隨機選取k個數,x1~k 2.判斷是否存在gcd(xi-xj,N)>1,若存在,則顯然gcd(xi-xj,N)是N的一個因數
同時也出現了一個問題,就是我們需要選取大約(N¼)個數,內存顯然是不可能夠的,那么又要如何解決這個問題呢?於是Pollard-rho算法就出現了。
Pollard Rho算法
因為數字太多內存不夠,所以Pollard-rho只把連續的兩個數存在內存中。
也就是說,我們並不會選出k個數,而是一個一個地生成偽隨機數,並檢查連續的兩個數是否符合條件。
我們會用一個函數來生成偽隨機數,就是這個→f(x)=(x2+a)%N
其中的a可以自己指定或rand()生成,但是這樣也伴隨了一個問題就是這個函數生成的偽隨機數還是有規律的,會無限循環。
於是就會出現像希臘字母ρ一樣的情況,這也是為什么這個算法名字中含有rho
那么我們又要如何避免這種情況呢?
首先為了保證沒有答案可能被遺漏,那么至少要把這個環完整的掃一遍。
聯想一下一個比較常見的問題,就是小學數學題做過的兩個人在環形操場上跑步,在同時起跑的情況下,當速度快的那個人追上速度慢的那個人的時候,一定已經多跑了一圈,也就是說此時兩人肯定都至少跑完了一圈,恰好符合我們的要求。
那么就是說我們要用兩個變量來存儲,一個用v的速度掃描環,一個用2v的速度,如果當兩個變量相等時還沒有找到答案,就退出這個環,重新取隨機數,再次帶入上面提到的函數中。
這里有一點要說明一下,就是為什么快的速度一定要是慢的速度的兩倍而不能更大。
假設快的速度為kv(k>2),整個環的路程為s,快的追上慢的所需時間為t,那么我們可以列出式子:
kvt−vt = s ⇒ (k−1)vt = s ⇒ vt = sk−1
因為 k > 2,所以 k-1 > 1,那么就有 s/(k−1) < s
,也就意味着此時用來判斷答案是否符合條件的(也就是速度較慢的那個)還沒有掃描完整個環,那么就有可能會有答案被遺漏。
以上就是完整的Pollard-rho算法過程,接下來上代碼
void Pollard_rho(long long N){ long long a=rand()%N+1;//隨機生成常數a long long x1,x2,d; x1=x2=rand()%N+1; while(1){ x1=count(x1,cc);x2=count(count(x2,cc),cc); if(x1==x2) {x1=x2=rand()%N+1;cc=rand()%N+1;} d=gcd(abs(x2-x1),N); if(d>1&&d<N){p=d;q=N/p;return;}//p,q記錄N的因數 } return; }