Miller_Rabin隨機素數測試+pollard_rho大數質因子分解(附例題Prime Test POJ - 1811)
阿新 • • 發佈:2018-10-31
一、合數的Miller_Rabin測試:
理論基礎:
- 費馬小定理:設p是素數,a是任意整數且a與p互質,則a^(p-1)≡1(mod p).
- 二次探測定理:設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)【中心思想】拉賓-米勒素數測試:設n是奇數,記
,q是奇素數,對不被n整除的某個a,如果下述兩個條件有一個成立,則n是素數
①
②對於i=0,1,2……k-1,至少有一個i符合
(如果n是個合數,則1~n-1之間至少有75%的數可作為n的拉賓-米勒證據,則經過10次測試可將成功率提高至0.999999046)快速積取模、快速冪取模。
演算法過程:
- n<2時不是素數,n==2是素數,n是偶數時不是素數;(特判)
- 將n-1分解為 ;
- 用rand()函式隨機取一個正整數a,且1<a<n;
- 根據上文理論基礎3.②從i=0開始檢測,共迴圈k次,若有符合的i,返回false,則證明是合數,迴圈結束後無i符合,返回true,證明是素數;
- 反覆進行8~10次,可將正確率接近於100%;
- 多次測試均符合,則可確定n是個素數。
二、pollard_rho大數質因子分解:
演算法過程(轉):
給定:整數n(已知是合數);
目標:找到一個因子 d | n;
步驟:
- 固定整數B
- 選擇一個整數k,k是大部分(或者全部)b的乘積滿足b≤B;
- 選擇一個隨機整數a滿足2 ≤ a ≤ n-2
- 計算
- 計算
- 如果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;
}