素數測試的基礎是費馬小定理:
對于素數p,任意0<a<p,有;變化形式為
。
費馬小定理逆定理不成立,所以費馬測試不保證正確。強化版測試方法就是Miller-Rabin素數測試:
在測試通過,且p-1還有因數2的情況下,進一步測試
,如此重復除2的測試直到指數不再被2整除或者mod余數不再是1。最后那次測試如果mod結果不是1和p-1則可否定p是素數。
從費馬測試到Miller-Rabin測試的理論依據,網上很多博文都瞎抄了個二次探測定理,然而根本不適用,實際的推論過程如下。
初次測試指數,其后每次測試都除2,依次記為
。
對于,如果可以繼續除2測試,即
,那么可以做如下變換
將mod改用整除形式表示
因為前提p是素數,無法再拆分因數,所以若左邊兩個括號不是直接等于p和c,就只能將c拆分因數
即左邊兩個括號必有一個是p的整數倍,若是前者,則;若是后者,則
。
特殊情況左邊有一個括號是0,那么,同樣符合上述結論。
附一定范圍內Miller-Rabin素數測試的a值選擇,摘自wiki:
?(16bit符號數范圍內確保素數判斷正確)
(32bit符號數范圍內確保素數判斷正確)
(64bit符號數范圍內確保素數判斷正確)
c代碼實現:
int fast_power_mod(int base, int exp, int divisor) {
? ? int rmd = 1;
? ? while (exp != 0) {
? ? ? ? if (exp & 0x1)
? ? ? ? ? ? rmd = (rmd * base) % divisor;
? ? ? ? exp = exp >> 1;
? ? ? ? base = (base * base) % divisor;
? ? }
? ? return rmd;
}type_prime miller_rabin(int num) {
? ? //special case for small number
? ? if?(num?<=?0)?return ILLEGAL;
????switch (num) {
? ? case 1:
? ? ? ? return ONE;
? ? case 2:
? ? case 3:
? ? ? ? return PRIME;
? ? default:
? ? ? ? break;
? ? }
? ? //for num < 4759123141, which means all int value
? ? const int TEST_BASE[] = {2, 7, 61}
? ? for (int i = 0; i < sizeof(TEST_BASE) / sizeof(TEST_BASE[0]); i++) {
????????int?base?=?TEST_BASE[i];
? ? ? ? if?(base?>=?num)?return?PRIME;
? ? ? ? int?exp?=?num?-?1;
? ? ? ? while?(true)?{
? ? ? ? ? ? int?rmd?=?fast_power_mod(base,?exp,?num);
? ? ? ? ? ? if?(rmd?==?num?-?1)?break;
????????????if?(rmd?!=?1)?return?COMPOSITE;
????????????//to?here?means?rmd==1
? ? ? ? ? ? if?((exp?&?0x1)?==?1)?break;
? ? ? ? ? ? exp?=?exp?>>?1;
? ? ? ? }
? ? }
? ? return PRIME;
}