1. 程式人生 > >米勒-拉賓素性檢測演算法

米勒-拉賓素性檢測演算法

米勒-拉賓素性檢測就是目前應用比較廣的一種隨機化素性檢測演算法。 

它是基於下面兩個定理:

  • (費馬小定理)如果 p 為素數,且 a 無法被 p 整除,則對於所有大於0小於 p 的整數 a,有
    ap11modp
  • 如果1在模n下有非平凡平方根,即存在x ≠ ±1 滿足 x21modn 則n必為合數。

上面兩個定理的具體證明就不給出了。
在素性檢測中,實際上我們利用的是費馬小定理的逆命題,也就是滿足該等式的整數都是素數。費馬小定理的逆命題是偽命題,但它在大多數情況下是成立的。當a = 2時,前十億個正整數中能滿足該等式的合數只有5597個(這些數被稱為偽素數)。
所以如果我們可以通過驗證等式a

n11(modn)是否成立去檢驗一個數是否為素數。對於10億以內的正整數,這樣做出錯的概率只有0.011%. 而通過多次改變a的值來進行檢驗,還可以進一步降低出錯的概率。
當然,這個方法還是有不少漏網之魚的。為了提高準確率,我們就需要用到上面的第二條定理了。
我們先將 p-1 表示為 u*2^t 的形式。那麼,a^(p-1) mod n 就可以表示為:(au)2tmodn
首先,我們計算x0=aumodn,然後使用反覆平方法計算
x1x20modn
x2x21modn

xix2i1modn

xtx2t1modn

很顯然,xi1xi在模n下的平方根,那麼,根據定理2,在這一計算過程中,如果有任意一個x
i
1modn
xi1±1modn,則n必為合數。

下面是C++程式碼實現(根據《演算法導論》中的虛擬碼編寫):

bool witness(int a, int n)
{
	unsigned int x = n - 1, t = 0;

	for(unsigned int i = 1; i <<= 1, t++) //計算t的值
		if((x | i) == x)
			break;
		
	unsigned int x0 = mod_exp(a, x >> t, n); //u = x >> t,x0 = a^u mod n
	for(int i = 0; i < t; i++)
	{
		x = x0 * x0 % n;
		if(x == 1 && x0 != 1 && x0 != n - 1) //x0是1的非平凡平方根,則n必為合數
			return true;

		x0 = x;
	}
	if(x != 1)  //不符合費馬小定理,n必為合數
		return true;

	return false;
}

bool is_prime(int n, int s) //s為檢測的次數,s越大準確度越高,但也越耗時間
{
	srand(time_t(time(NULL)));
	for(int i = 0; i < s; i++)
	{
		int a = rand() % (n - 2) + 2;//實際上隨機生成的a是不允許重複的,這樣寫只是為了簡便

		if(witness(a, n))  //如果n為合數,直接返回檢測結果
			return false;
	}
	return true;
}