淺談Miller-Rabin素數檢測
對於素數判斷的操作,我們通常使用的是時間復雜度為\(O(\sqrt N)\)的試除法。按理說這種復雜度已經是較優秀的了,但是假如給定的需要判斷的數極其之大,並且給定的時限不夠以\(O(\sqrt N)\)的試除法來判斷,該怎么辦?
題出錯了
想得美。
於是,今天的主角出場了:Miller-Rabin素數檢測。
Miller-Rabin素數檢測算法用於在短時間內判斷出一個數是否是質數,時間復雜度比試除法優秀,應該是\(O(T\times \log N)\)級別的(T為檢測輪數)。
Miller-Rabin算法的實現原理
其實,試除法是目前能實現的百分百正確率判斷質數的最快方法。Miller-Rabin之所以能夠比它更快,是在犧牲了正確率的基礎上。(汗)但是千萬不要質疑這個算法的正確性。這種有一定容錯率的算法統稱為概率型算法,它們的特點就是不保對,但是用起來的確非常的爽。並且,它們的容錯率也非常低,可以用多次檢測來彌補。
經過理論證明,Miller-Rabin的錯誤率是\(4^{-T}\),T是檢測輪數,一般來講,當\(T\)達到50左右時,就可以將錯誤率降到非常低的程度。甚至,比計算機本身出錯的概率還要低。
那么,在了解了Miller-Rabin的隨機性之后,我們接下來要學習它的實現原理。
Miller-Rabin的實現原理利用了費馬小定理。費馬小定理的內容是:
(\(p\)為質數)
如果想詳細了解費馬小定理,敬請參考百度百科。
那么,這個費馬小定理就可以作為素數性的一個判斷依據。往簡單說,就是針對一個大整數,如果\(a^{p-1}\%p=1\),那么這個數有很大可能是質數。反之,那么這個數一定不是質數。
好啦!那就拿\(a^{p-1}\%p\),快速冪取模一下看一看就得了!
這當然是錯的。首先,這種方式的錯誤率很高,你需要反復地用不同的a檢測很多次,時間復雜度噌噌上,最后還有可能依然是錯誤答案。所以我們應該在費嗎小定理的基礎上再加一個約束條件,使得算法能夠穩定、有序的輸出正確解。
介紹下一個理論依據:二次探測定理。
二次探測定理的內容是:
如果一個數\(p\)是質數,對於一個\(x\in (0,p)\)且\(x\in Z\),方程\(x^2 \equiv 1\,\,\,\,(mod\,\,p)\)的解有且只有兩個:\(x=1\)或\(x=p-1\)。
那么,在快速冪累乘的基礎上,反復判斷現在的\(p\)是否符合二次探測定理,就大大增加了其正確性。
具體的實現方式是:
如果一個數\(p\)是質數,那么\(p-1\)一定會是個偶數(你不要拿2來剛我),那么,對於這個指數\(p-1\),我們可以將其分解成\(m\times 2^k\)的形式,其中\(m\)為奇數。那么根據快速冪的原理,我們就會依次對以下數列進行二次探測定理的檢測:
如果這些都合法,最后用費馬小定理判斷一下是否合法即可。
Miller-Rabin算法的模板
bool Miller_check(int a,int n)
{
int ret=1;
int b=n-1;
while(b)
{
if(b&1)
ret=(ret*a)%n;
int x=a;//采用臨時變量保存改變前的a
a=(a*a)%n;
if(a==1 && x!=1 && x!=n-1)//當a為1的時候,方程成立,開始判斷解。
return 0;//不是素數
b>>=1;
}
return (ret==1)?1:0;
}
bool Miller_Rabin(int n,int t)
{
if(n==2)
return 1;
if(n<2 || !(n&1))
return 0;
while(t--)
{
srand(time(NULL));
int a=rand();//a要隨機數
if(!Miller_check(a,n))
return 0;
}
return 1;
}
要注意的是,因為我們普遍用Miller-Rabin檢測大數,所以在實際應用的時候往往要開long long。代碼為了美觀簡化就刪去了這個步驟,請應用的時候一定要加上。
Miller-Rabin算法大體就是這樣!謝謝大家的觀看!!