1. 程式人生 > >POJ1811 Prime Test (Pollard-Rho演算法) Sunshine大神

POJ1811 Prime Test (Pollard-Rho演算法) Sunshine大神

很久沒有寫部落格了。。。最近軍訓加開學,感覺刷題速度有降低,要補一補。

迴歸正題,正式進入數論階段,討論一下關於素數判定的那些事。

一類問題: 判定一個整數n(n>1)是否為素數。

演算法1:

直接根據素數的定義列舉i2(n1),如果n%i==0n為合數。
時間複雜度:O(n)

<code class="language-cpp hljs  has-numbering"><span class="hljs-keyword">bool</span> is_prime(<span class="hljs-keyword">int</span> n) {
    <span class="hljs-keyword">int</span> i;
    <span class="hljs-keyword">for</span>(i = <span class="hljs-number">2</span>; i < n; i++)
        <span class="hljs-keyword">if</span>(n % i == <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

演算法2:

發現若存在i<n使得n%i==0,則必有n%(n/i)==0
所以只需列舉i2sqrt(n)即可。
時間複雜度:O(n)

<code class="language-cpp hljs  has-numbering"><span class="hljs-keyword">bool</span> is_prime(<span class="hljs-keyword">int</span> n) {
    <span class="hljs-keyword">int</span> i;
    <span class="hljs-keyword">for</span>(i = <span class="hljs-number">2</span>; i * i <= n; i++)
        <span class="hljs-keyword">if</span>(n % i == <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

Miller-Rabin演算法:

這是一種隨機性素數判定演算法,也就是說,答案可能出錯,但是可能性極小。

先是講兩個定理:

費馬小定理:
對於一個質數p,取任意整數a,滿足gcd(p,a)=1,則有

ap11(modp)

二次探測定理:
對於0<x<p,若p是素數,則方程:

x21(modp)
的解為:
x1=1,x2=p1

因為費馬小定理的逆命題不成立,而否逆命題成立,所以我們可以利用一下一點:
對於任意整數a<p,不滿足ap11(modp),則p為合數。

所以我們可以不斷在區間[2,p1]範圍內隨機取a,並進行判定。在s次判定不為合數之後,我們就可以說這個數是質數。

但是這還不夠精確,我們可以先把p1分解成2t×u(u{x|x=2k+1,kN})的形式,然後令x[0]=aumodp,,那麼將x[0]平方t次就是(au)2tmodp的值,我們設x[i]x[0]平方i次的值,根據二次探測定理,若x[i]等於1,則x[i1]等於1或p-1,不滿足則p為合數。

注意以上操作中所有的形如abmodp的式子都要用快速冪運算,當n比較大時,形如a×bmodp的式子也要使用分治的思想來計算。

這就是Miller-Rabin演算法的主要內容。

時間複雜度:考慮常數後為O(slog3n)

程式碼如下:

<code class="hljs cpp has-numbering"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MAXN = <span class="hljs-number">65</span>;
<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> n, x[MAXN];

<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> multi(<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> a, <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> b, <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> p) {
    <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> ans = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">while</span>(b) {
        <span class="hljs-keyword">if</span>(b&<span class="hljs-number">1L</span>L) ans = (ans+a)%p;
        a = (a+a)%p;
        b >>= <span class="hljs-number">1</span>;
    }
    <span class="hljs-keyword">return</span> ans;
}

<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> qpow(<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> a, <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> b, <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> p) {
    <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> ans = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">while</span>(b) {
        <span class="hljs-keyword">if</span>(b&<span class="hljs-number">1L</span>L) ans = multi(ans, a, p);
        a = multi(a, a, p);
        b >>= <span class="hljs-number">1</span>;
    }
    <span class="hljs-keyword">return</span> ans;
}

<span class="hljs-keyword">bool</span> Miller_Rabin(<span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> n) {
    <span class="hljs-keyword">if</span>(n == <span class="hljs-number">2</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    <span class="hljs-keyword">int</span> s = <span class="hljs-number">20</span>, i, t = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> u = n-<span class="hljs-number">1</span>;
    <span class="hljs-keyword">while</span>(!(u & <span class="hljs-number">1</span>)) {
        t++;
        u >>= <span class="hljs-number">1</span>;
    }
    <span class="hljs-keyword">while</span>(s--) {
        <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> a = rand()%(n-<span class="hljs-number">2</span>)+<span class="hljs-number">2</span>;
        x[<span class="hljs-number">0</span>] = qpow(a, u, n);
        <span class="hljs-keyword">for</span>(i = <span class="hljs-number">1</span>; i <= t; i++) {
            x[i] = multi(x[i-<span class="hljs-number">1</span>], x[i-<span class="hljs-number">1</span>], n);
            <span class="hljs-keyword">if</span>(x[i] == <span class="hljs-number">1</span> && x[i-<span class="hljs-number">1</span>] != <span class="hljs-number">1</span> && x[i-<span class="hljs-number">1</span>] != n-<span class="hljs-number">1</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }
        <span class="hljs-keyword">if</span>(x[t] != <span class="hljs-number">1</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}</code>