1. 程式人生 > >Miller_Rabin隨機素數測試+pollard_rho大數質因子分解(附例題Prime Test POJ - 1811)

Miller_Rabin隨機素數測試+pollard_rho大數質因子分解(附例題Prime Test POJ - 1811)

一、合數的Miller_Rabin測試:

理論基礎:

  1. 費馬小定理:設p是素數,a是任意整數且a與p互質,則a^(p-1)≡1(mod p).
  2. 二次探測定理:設p是素數,x是小於p的正整數,且x^2≡1(modp) , 則x = 1 或 x = p - 1;
    (易證:x^2≡1(mod p)   →  x^2-1≡(mod p)  →  (x+1)(x-1)≡ 0 (mod p)  →  (x+1)=p,(x-1)= 0  →  x = 1 或 x = p - 1)
  3. 【中心思想】拉賓-米勒素數測試:設n是奇數,記n-1=2^{k}*q

    ,q是奇素數,對不被n整除的某個a,如果下述兩個條件有一個成立,則n是素數
    a^{q}\equiv 1(modn)
    ②對於i=0,1,2……k-1,至少有一個i符合a^{2^{i}*q}\equiv -1(mod n)

    (如果n是個合數,則1~n-1之間至少有75%的數可作為n的拉賓-米勒證據,則經過10次測試可將成功率提高至0.999999046)

  4. 快速積取模、快速冪取模。

演算法過程:

  1. n<2時不是素數,n==2是素數,n是偶數時不是素數;(特判)
  2. 將n-1分解為 n-1=2^{k}*q
  3. 用rand()函式隨機取一個正整數a,且1<a<n;
  4. 根據上文理論基礎3.②從i=0開始檢測,共迴圈k次,若有符合的i,返回false,則證明是合數,迴圈結束後無i符合,返回true,證明是素數;
  5. 反覆進行8~10次,可將正確率接近於100%;
  6. 多次測試均符合,則可確定n是個素數。
     

 二、pollard_rho大數質因子分解:

 演算法過程(轉):

給定:整數n(已知是合數);

目標:找到一個因子 d | n;

步驟:

  1. 固定整數B
  2. 選擇一個整數k,k是大部分(或者全部)b的乘積滿足b≤B;
  3. 選擇一個隨機整數a滿足2 ≤ a ≤ n-2
  4. 計算 r=a^{k}(modn)
  5. 計算d=gcd(r-1,n)
  6. 如果d = 1 或者 d = n,返回步驟 2 ,否則,d就是要找的因子。

 附例題POJ1811(Prime Test )程式碼及程式碼詳解:

#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <time.h>
using namespace std;
const int S = 10; //隨機演算法判定次數
// 計算 ret = (a*b)%c a,b,c < 2^63
long long mult_mod(long long a,long long b,long long c)
{
    a %= c;
    b %= c;
    long long ret = 0;
    long long tmp = a;
    while(b)
    {
        if(b & 1)
        {
            ret += tmp;
            if(ret > c)
                ret-= c;//直接取模慢很多
        }
        tmp <<= 1;
        if(tmp > c)
            tmp-= c;
        b >>= 1;
    }
    return ret;
}
// 計算 ret = (a^n)%mod(快速冪)
long long pow_mod(long long a,long long n,long long mod)
{
    long long ret = 1;
    long long temp = a%mod;
    while(n)
    {
        if(n & 1)
            ret = mult_mod(ret,temp,mod);
        temp = mult_mod(temp,temp,mod);
        n >>= 1;
    }
    return ret;
}
//通過a^(n-1)同餘1(mod n)來判斷是不是素數
// n-1=x*2^t 中間使用二次判斷
//是合數返回true,不一定是合數返回false;
bool check(long long a, long long n ,long long x,long long t)
//a為一隨機數,n為待檢測數也是模,x為(n-1)除去2的倍數後的值,t為n-1所能分解出的2
{
    // n-1=x*2^t;
    long long ret = pow_mod(a,x,n);
    long long last = ret;
    for(int i = 1; i <= t; i++)
    {
        ret = mult_mod(ret,ret,n);
            if(ret== 1&&last!=1&&last!=n-1)
            return true;//合數
        last = ret;
    }
    if(ret != 1)
        return true;
    else
        return false;
}
//**************************************************
// Miller_Rabin 演算法
// 是素數返回 true,(可能是偽素數),不是素數返回 false;
//**************************************************
bool Miller_Rabin(long long n)
{
    if( n < 2)
        return false;
    if( n == 2)
        return true;
    if( (n&1) == 0)
        return false;//偶數
    long long x = n - 1;
    long long t = 0;
    while( (x&1)==0 )
    {
        x >>= 1;
        t++;//x能分解出t個2。
    }
    srand(time(NULL));//隨機數發生器的初始化函式
    for(int i = 0; i < S; i++)//進行S次檢測,減少誤差.
    {
        long long a = rand()%(n-1) + 1;//取 1<= a <N的隨機數
        if( check(a,n,x,t) )
            return false;
    }
    return true;
}

//**********************************************
// pollard_rho 演算法進行質因素分解
//**********************************************
long long factor[100];//質因素分解結果(剛返回時時無序的)
int tol;//質因數的個數 (0~n-1)

long long gcd(long long a,long long b)//歐幾里得求最大公約數
{
    long long t;
    while(b)
    {
        t = a;
        a = b;
        b = t%b;
    }
    if(a >= 0)
        return a;
    else
        return -a;
}

//找出一個因子
long long pollard_rho(long long x,long long c)
{
    long long i = 1, k = 2;
    srand(time(NULL));
    long long a = rand()%(x-1) + 1;
    long long y = a;
    while(1)
    {
        i ++;
        a = (mult_mod(a,a,x) + c)%x;
        long long d = gcd(y - a,x);
        if( d != 1 && d != x)
            return d;
        if(y == a)
            return x;
        if(i == k)
        {
            y = a;
            k += k;
        }
    }
}
//對 n 進行素因子分解,存入 factor. k 設定為 107 左右即可
void findfac(long long n,int k)
{
    if(n == 1)
        return;
    if(Miller_Rabin(n))
    {
        factor[tol++] = n;
        return;
    }
    long long p = n;
    int c = k;
    while( p >= n)
        p = pollard_rho(p,c--);//值變化,防止死迴圈 k
    findfac(p,k);
    findfac(n/p,k);
}
int main()
{
    int T;
    long long n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d",&n);
        if(Miller_Rabin(n))
            printf("Prime\n");
        else
        {
            tol = 0;
            findfac(n,107);
            long long ans = factor[0];
            for(int i = 1; i < tol; i++)
                ans = min(ans,factor[i]);
            printf("%I64d\n",ans);
        }
    }
    return 0;
}