POJ 1811 Prime Test (Rabin-Miller強偽素數測試 和Pollard-rho 因數分解)

題目鏈接

Description

Given a big integer number, you are required to find out whether it's a prime number.

Input

The first line contains the number of test cases T (1 <= T <= 20 ), then the following T lines each contains an integer number N (2 <= N < 254).

Output

For each test case, if N is a prime number, output a line containing the word "Prime", otherwise, output a line containing the smallest prime factor of N.

Sample Input

2
5
10

Sample Output

Prime
2

分析:
給定一個小于2^54的整數,判斷該數是不是素數,如果是素數的話,輸出“Prime”,否則輸出該數的最小的素因子。熟悉的有關素數的算法在這道題看來都還是太弱了,所以得額外的找方法來解決。

應用到的兩個重要算法是Rabin-Miller強偽素數測試和Pollard ?因數分解算法。前者可以在 的時間內以很高的成功概率判斷一個整數是否是素數。后者可以在最優 的時間內完成合數的因數分解。這兩種算法相對于試除法都顯得比較復雜。本文試圖對這兩者進行簡單的闡述,說明它們在32位計算機上限制在64位以內的條件下的實現中的細節。下文提到的所有字母均表示整數。

一、Rabin-Miller強偽素數測試
Rabin-Miller強偽素數測試的基本思想來源于如下的Fermat小定理:
如果p是一個素數,則對任意a有 (a^p)%p=a。特別的,如果p不能整除a,則還有(a^(p-1))%p=1 。
利用Fermat小定理可以得到一個測試合數的有力算法:對n>1,選擇a>1,計算(a^(n-1))%n,若結果不等于1則n是合數。若結果等于1則n可能是素數,并被稱為一個以a為基的弱可能素數(weak probable prime base a,a-PRP);若n是合數,則又被稱為一個以a為基的偽素數(pseudoprime)。

這個算法的成功率是相當高的。在小于25,000,000,000的1,091,987,405個素數中,一共只用21,853個以2為基的偽素數。但不幸的是,Alford、Granville和Pomerance在1994年證明了存在無窮多個被稱為Carmichael數的整數對于任意與其互素的整數a算法的計算結果都是1。最小的五個Carmichael數是561、1,105、1,729、2,465和2,801。

考慮素數的這樣一個性質:若n是素數,則1對模n的平方根只可能是1和-1 。因此a^(n-1) 對模n的平方根 a^((n-1)/2)也只可能是1和-1 。如果(n-1)/2本身還是一個偶數,我們可以再取一次平方根……將這些寫成一個算法:
(Rabin-Miller強偽素數測試)記n-1=(2^s)d,其中d是奇數而s非負。如果(a^d)%n=1 ,或者對某個 0<=r<s有(a^((2^r)d))%n=-1 ,則認為n通過測試,并稱之為一個以a為基的強可能素數(strong probable prime base a,a-SPRP)。若n是合數,則又稱之為一個以a為基的強偽素數(strong pseudoprime)。

這個測試同時被冠以Miller的名字是因為Miller提出并證明了如下測試:如果擴展黎曼猜想(extended Riemann hypothesis)成立,那么對于所有滿足1<a<2(log n)^2 的基a,n都是a-SPRP,則n是素數。
盡管Monier和Rabin在1980年證明了這個測試的錯誤概率(即合數通過測試的概率)不超過 1/4,單個測試相對來說還是相當弱的(Pomerance、Selfridge和Wagstaff, Jr.證明了對任意a>1都存在無窮多個a-SPRP)。但由于不存在“強Carmichael數”(任何合數n都存在一個基a試之不是a-SPRP),我們可以組合多個測試來產生有力的測試,以至于對足夠小的n可以用來證明其是否素數。

取前k個素數為基,并用 來表示以前k個素數為基的強偽素數,Riesel在1994年給出下表:

1149206-20180410154003894-496092312.png

考慮到64位二進制數能表示的范圍,只需取前9個素數為基,則對小于φ8 的所有大于1的整數測試都是正確的;對大于或等于 φ8并小于2^64 的整數測試錯誤的概率不超過1/(2^18) 。

Rabin-Miller強偽素數測試本身的形式稍有一些復雜,在實現時可以下面的簡單形式代替:
對 n>1,如果(a^(n-1))%n=1則認為n通過測試。
代替的理由可簡單證明如下:
仍然記n-1=(2^s)d ,其中d是奇數而s非負。若n是素數,由(a^(n-1))%n=1可以推出(a^(2^(s-1)d))%n=1=(a^((n-1)/2))%n或(a^(2^(s-1)d))%n=-1=(a^((n-1)/2))%n 。若為前者,顯然取r=s-1 即可使n通過測試。若為后者,則繼續取平方根,直到對某個 1<=r<s有 (a^((2^r)d))%n=-1,或(a^(2d))%n=1 。無論 (a^d)%n=1還是 a^d)%n=-1,n都通過測試。

Rabin-Miller強偽素數測試的核心是冪取模(即計算 )(a^s)%n。計算冪取模有以下的 O(log n)算法(以Sprache偽代碼語言描述):
這個算法在32位計算機上實現的難點在于指令集和絕大部分編程語言的編譯器都只提供了32位相乘結果為64位的整數乘法,浮點運算由于精度的問題不能應用于這里的乘法。唯一解決辦法是模仿一些編譯器內建的64位整數乘法來實現兩個無符號64位相乘結果為128位的乘法。這個乘法可以將兩個乘數分別分割成兩個32位數來實現。為方便乘法之后的取模運算,運算結果應當用連續的128個二進制位來表示。以下是其偽代碼:
1149206-20180410154748025-186409617.png

乘法之后的取模運算可以用浮點運算快速完成。具體做法是乘積的高64位和低64位分別先對除數取模,然后再利用浮點單元合并取模。這里的浮點運算要求浮點單元以最高精度運算,計算前應先將浮點單元控制字中的精度控制位設置為64位精度。為保證精度,應當用80位浮點數實現此運算。偽代碼如下:
1149206-20180410154835664-641442622.png

至此,Rabin-Miller強偽素數測試的核心已經全部實現。

二、Pollard-rho 因數分解算法
Pollard-rho因數分解算法又稱為Pollard Monte Carlo因數分解算法。它的核心思想是:選取一個隨機數a。再選一個隨機數b。檢查 gcd(a-b,n)是否大于1。若大于1, gcd(a-b,n)就是n的一個因子。若不大于1,再選取隨機數c,檢查 gcd(c-b,n)和 gcd(c-a,n)。如此繼續,直到找到n的一個非平凡因子。

1149206-20180410155145481-1959778883.png

1149206-20180410155200326-875392891.png

代碼:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <time.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll __int64//****************************************************************
// Miller_Rabin 算法進行素數測試
//速度快,而且可以判斷 <2^63的數
//****************************************************************
const int S=20;//隨機算法判定次數,S越大,判錯概率越小//計算 (a*b)%c.   a,b都是long long的數,直接相乘可能溢出的
/*
ll Mult_mod (ll a,ll b,ll c)   //  a,b,c <2^63
{a%=c;b%=c;ll ret=0;while (b){if (b&1) {ret+=a;ret%=c;}a<<=1;if (a>=c)a%=c;b>>=1;}return ret;
}*/ll Mult_mod (ll a,ll b,ll c)  //減法實現比取模速度快
{//返回(a*b) mod c,a,b,c<2^63a%=c;b%=c;ll ret=0;while (b){if (b&1){ret+=a;if (ret>=c) ret-=c;}a<<=1;if (a>=c) a-=c;b>>=1;}return ret;
}//計算  x^n %c
ll Pow_mod (ll x,ll n,ll mod) //x^n%c
{if (n==1) return x%mod;x%=mod;ll tmp=x;ll ret=1;while (n){if (n&1) ret=Mult_mod(ret,tmp,mod);//(a*b)%ctmp=Mult_mod(tmp,tmp,mod);n>>=1;}return ret;
}//以a為基,n-1=x*2^t      a^(n-1)=1(mod n)  驗證n是不是合數
//一定是合數返回true,不一定返回false
bool Check (ll a,ll n,ll x,ll t)
{ll ret=Pow_mod(a,x,n);//(a^x)%nll 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;return false;
}// Miller_Rabin()算法素數判定
//是素數返回true.(可能是偽素數,但概率極小)
//合數返回false;bool Miller_Rabin (ll n)
{if (n<2) return false;if (n==2) return true;if ((n&1)==0) return false;//偶數ll x=n-1;ll t=0;while ((x&1)==0)//不斷的對于x進行右移操作{x>>=1;t++;}for (int i=0; i<S; i++){ll a=rand()%(n-1)+1; //rand()需要stdlib.h頭文件if (Check(a,n,x,t))return false;//合數}return true;
}//************************************************
//pollard_rho 算法進行質因數分解
//************************************************ll factor[100];//質因數分解結果(剛返回時是無序的)
int tol;//質因數的個數。數組下標從0開始ll Gcd (ll a,ll b)
{if (a==0) return 1;   if (a<0) return Gcd(-a,b);while (b){ll t=a%b;a=b;b=t;}return a;
}ll Pollard_rho (ll x,ll c)
{ll i=1,k=2;ll x0=rand()%x;ll y=x0;while (true){i++;x0=(Mult_mod(x0,x0,x)+c)%x;ll d=Gcd(y-x0,x);if (d!=1 && d!=x) return d;if (y==x0) return x;if (i==k){y=x0;k+=k;}}
}
//對n進行素因子分解
void Findfac (ll n)
{if (Miller_Rabin(n)) //素數{factor[tol++]=n;return;}ll p=n;while (p>=n) p=Pollard_rho(p,rand()%(n-1)+1);Findfac(p);Findfac(n/p);
}int main ()  // Poj 1811 交G++ 比c++ 快很多
{// srand(time(NULL));//需要time.h頭文件  //POJ上G++要去掉這句話int T;scanf("%d",&T);while (T--){ll n;scanf("%I64d",&n);if (Miller_Rabin(n)){printf("Prime\n");continue;}tol=0;Findfac(n);ll ans=factor[0];for (int i=1; i<tol; i++)if (factor[i]<ans)ans=factor[i];printf("%I64d\n",ans);}return 0;
}

轉載于:https://www.cnblogs.com/cmmdc/p/8779801.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/250571.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/250571.shtml
英文地址,請注明出處:http://en.pswp.cn/news/250571.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Windows忘記mysql的密碼

1、查看mysql的安裝路徑 show variables like "%char%"; 路徑&#xff1a;C:\Program Files\MySQL\MySQL Server 5.7\ 2、關閉mysql服務 我的電腦--管理--服務于應用程序--服務--mysql--右鍵--停止 4、開始修改密碼 1、打開dos窗口&#xff1a; widR 2.將目錄mysqld.…

Java 的單例模式

Java 的單例模式 單例模式(Singleton) 單例設計模式&#xff0c;就是采取一定的方法保證在整個的軟件系統中&#xff0c;對某個類只能存在一個對象實例&#xff0c;并且該類只提供一個取得其對象實例的方法。如果我們要讓類在一個虛擬機中只能產生一個對象&#xff0c;我們首…

react --- 隔代傳遞參數的三種方式

組件跨層級通信 - Context 上下文提供一種不需要每層設置props就能跨多級組件傳遞數據的方式 方式1 Provider提供值Consumer來消費傳遞的值 import React from react;// 創建一個上下文 const Mycontext React.createContext(); const { Provider, Consumer } MyContext;…

bzoj 4898: [Apio2017]商旅【Floyd+分數規劃+二分】

其實并不會分數規劃 因為要最大化 ans總收益/總路程 &#xff0c;所以考慮二分答案&#xff0c;找到一條 ans<總收益/總路程 的回路。先預處理出d(i,j)為(i,j)最短路&#xff0c;w(i,j)為在i買某個物品在j賣出的最大收益&#xff08;最小為0&#xff09;。把式子變一下&…

幾種鏈表的優缺點比較

轉載于:https://www.cnblogs.com/FengZeng666/p/9425117.html

node --- 模擬express實現一個簡單的服務器

目標 使用express實現一個監聽3000端口的http服務如下 const express require(express); const app express();app.get(/, (req, res) > {res.end(Hello Express); }) app.get(/users,(req, res)>{res.end(JSON.stringify({name: abc})) }) app.listen(3000, ()>{…

node --- [跨域] 預檢請求

簡單請求 若滿足所有下述條件&#xff0c;則該請求可視為“簡單請求”&#xff1a; 使用下列方法之一&#xff1a; GET HEAD POST Content-Type: (僅當POST方法的Content-Type值等于下列之一才算做簡單需求) text/plain multipart/form-data application/x-www-form-ur…

Java 的異常

Java 的異常 異常&#xff1a;在Java語言中&#xff0c;將程序執行中發生的不正常情況稱為“異常”。(開發過程中的語法錯誤和邏輯錯誤不是異常)Java程序在執行過程中所發生的異常事件可分為兩類&#xff1a; Error: Java虛擬機無法解決的嚴重問題。如&#xff1a;JVM系統內部…

docker --- 將已有的項目發布到云端

[運行在win10] Dockerfile Docker根據該文件生成image文件 FROM node:8.4 COPY . /app WORKDIR /app RUN ["npm", "install"] EXPOSE 3000/tcp根據Dockerfile生成image 注意末尾有個.(英文的點)代表當前目錄 docker image build -t koa-demo:0.0.1 .查…

傳遞動態內存

一、內存分配分類 1.從靜態存儲區域分配。內存在程序編譯的時候就已經分配好&#xff0c;這塊內存在程序的整個運行期間都存在。例如全局變量&#xff0c;static 變量。 2.在棧上創建。在執行函數時&#xff0c;函數內局部變量的存儲單元都可以在棧上創建&#xff0c;函數執行結…

linux --- 基礎指令

基礎命令 1、ls(list) 用法1: # ls 含義: 列出當前工作目錄下所有的 文件/文件夾 的名稱 用法2: # ls 路徑 含義: 列出指定路徑目錄下所有的 文件/文件夾 的名稱 用法3: # ls 選項 路徑 含義: 以指定的格式來顯示指定目錄下文件夾的名稱 栗子: # ls -l 路徑 -->> 表…

驗證碼功能

驗證碼功能 1.安裝captcha插件 (dj_login) D:\dj\dj_login>pip install django-simple-captcha Collecting django-simple-captchaUsing cached https://files.pythonhosted.org/packages/d7/f4/ea95b04ed3abc7bf225716f17e35c5a185f6100db4d7541a 46696ce40351/django-simp…

Java 類的成員

Java 類的成員 初始化塊 1、一個類中初始化塊若有修飾符&#xff0c;則只能被static修飾&#xff0c;稱為靜態代碼塊(staticblock )&#xff0c;當類被載入時&#xff0c;類屬性的聲明和靜態代碼塊先后順序被執行&#xff0c;且只被執行一次。 2、static塊通常用于初始化sta…

linux --- 進階指令

進階指令(重點) 1、df 指令 作用: 查看磁盤空間語法: # df -h 注: -h:以較高可讀性的方式展示出來 2、free 指令 作用: 查看內存使用情況語法: # free -m 注: -m:以M的單位顯示內存情況 -/ buffers/cache: free 代表真實可用的內存為 486 Mb Swap: 表示,臨時將硬盤當作內存…

MFC對話框播放8位512*512的像素數據

關鍵代碼&#xff1a; UINT playAllFrame(LPVOID lpParameter){//showOneFrame(0,TRUE);CMFCDialogDlg *mydlg (CMFCDialogDlg *) lpParameter;//獲取原始數據文件CString selectPath;mydlg->GetDlgItemTextW(IDC_MFCEDITBROWSE,selectPath);string StrSelectPath(CW2A(sel…

java 集合 CopyOnWriteArrayList

CopyOnWriteArrayList 也是實現List接口他是在concurrent 包里面&#xff0c;所以他是線程安全的&#xff0c;其他的基本和ArrayList很想。他線程安全是用ReentrantLock 實現的&#xff0c;他內部有一個ReentrantLock對象&#xff0c;然后在增刪改的時候都操作這個鎖對象&#…

Java 類的特性1

Java 類的特性1 繼承 1.為什么要有繼承&#xff1f; 多個類中存在相同屬性和行為時&#xff0c;將這些內容抽取到單獨一個類中&#xff0c;那么多個類無需再定義這些屬性和行為&#xff0c;只要繼承那個類即可。 2.此處的多個類稱為子類&#xff0c;單獨的這個類稱為父類&a…

linux --- 高級指令

高級指令 1、hostname 指令 作用: 操作(讀取|操作)服務器的主機名語法1: # hostname (輸出完整的主機名) 語法2: # hostname -f (輸出當前主機中的FQDN) FQDN&#xff1a;(Fully Qualified Domain Name)全限定域名&#xff1a;同時帶有主機名和域名的名稱。 2、id 指令 作…

Linux修改密碼后不能SSH遠程登錄了

1、把以下文件的屬性改成755&#xff0c;然后再修改密碼&#xff1a;/etc/passwd ,/etc/group , /etc/shadow , /etc/gshadow2、如果文件的屬性無法更改&#xff0c;請用lsattr 查看文件是否有 i 屬性&#xff0c;如有&#xff0c;則用chattr取消之&#xff0c;如&#xff1a;l…

Java 類的特性2

Java 類的特性2 類屬性、類方法的設計思想 類屬性作為該類各個對象之間共享的變量。在設計類時,分析哪些類屬性不因對象的不同而改變&#xff0c;將這些屬性設置為類屬性。相應的方法設置為類方法。如果方法與調用者無關&#xff0c;則這樣的方法通常被聲明為類方法&#xff…