最簡單直觀簡單的素數判定方法就是試除法。對於判斷數n是否是素數,我們從2開始一直到sqrt(n)。如果找到一個因子則判斷n不是素數,否則是素數。代碼如下:
bool isPrime( long long n )
{
for(long long i = 2; i*i <= n; i++)
{
if(n%i == 0) return false;
}
return true;
}
如果要找到成1~n的所有素數那么這個時間代價就變為O(n^2),很多時候是不可接受的。
所以隨着學習的深入,我們了解到了素數篩法,即從2開始,2的倍數肯定不是素數,再向右掃描,如果掃描到素數,則重復之前的過程,剔除之后的部分合數(准確的說是關於當前質數的倍數),如果掃描到合數則跳過(表示前面已經更新過這個數不是素數)。然后都掃描一遍即可把1~n的素數求解出來。這個算法的復雜度略高於O(n)。素數篩代碼如下:
bool isprime[MAXN];
int prime[MAXN];
int cnt = 0;//保存素數個數
void getPrime()
{
for(int i = 1; i < MAXN; i++)
isprime[i] = true;
//先假設所有數是素數,后面逐個掃描更新
for(int i = 2; i < MAXN; i++) //掃一遍
{
if(!isprime[i]) continue;
//如果不是素數,則不往后面更新
prime[cnt++] = i;
for(int j = 2 * i; j < MAXN; j += i)
isprime[j] = false;
}
}
但是這個算法的弊端在於,為了判斷一個大數是否是素數必須從從頭開始掃描,而且空間代價也受不了,故不能離散的判斷。
Miller_rabin算法
算法的理論基礎:
1. Fermat定理:
若n是奇素數,a是任意正整數(1≤ a≤ n−1),
證明:由費馬定理,可以排除大部分非素數的情況(滿足費馬定理是素數的必要條件),給出一個奇素數n,顯然n-1為一個偶數,存在
,顯然
(q,m為任意整數)是成立的,所以
,顯然
.
2. 二次探測定理:

證明過程如下:

由p為一個素數可以推出
。
所以根據二次探測定理,可以推斷
,
……,
.
3. 綜上:
對於一個大數n,判斷n是不是素數的時候,可以先考慮a(n-1)≡ 1(mod n)
對於n-1,一定可以拆分成2s+d:

可以從x = ad開始,依次平方s次,每次平方的時候模上n,按照之前的平方根定理,如果模上n的結果為1的話,那么x一定是1,或者是n-1,如果不滿足則不是素數,x=x2,再次循環。
每次隨機選一個在2-n-1的數字作為a,可以重復測試。
由於mod上的是n,n是一個大數,所以快速冪中的乘法,需要用快速加法來實現。不然就算模上之后再相乘也會溢出。
代碼:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int times = 20;
int number = 0;
map<long long, int>m;
long long Random( long long n )
//生成[ 0 , n ]的隨機數
{
return ((double)rand( ) / RAND_MAX*n + 0.5);
}
long long q_mul( long long a, long long b, long long mod )
{//快速計算 (a*b) % mod
long long ans = 0;
while(b)
{
if(b & 1)
{
b--;
ans =(ans+ a)%mod;
}
b /= 2;
a = (a + a) % mod;
}
return ans;
}
long long q_pow( long long a, long long b, long long mod )
{//快速計算 (a^b) % mod
long long ans = 1;
while(b)
{
if(b & 1)
{
ans = q_mul( ans, a, mod );
}
b /= 2;
a = q_mul( a, a, mod );
}
return ans;
}
bool witness( long long a, long long n )//miller_rabin算法的精華
{//用檢驗算子a來檢驗n是不是素數
long long tem = n - 1;
int j = 0;
while(tem % 2 == 0)
{
tem /= 2;
j++;
}
//將n-1拆分為a^r * s
long long x = q_pow( a, tem, n );
//得到a^r mod n
if(x == 1 || x == n - 1) return true;
//余數為1則為素數
while(j--) //否則試驗條件2看是否有滿足的 j
{
x = q_mul( x, x, n );
if(x == n - 1) return true;
}
return false;
}
bool miller_rabin( long long n )
{//檢驗n是否是素數
if(n == 2)
return true;
if(n < 2 || n % 2 == 0)
return false;
//如果是2則是素數,如果<2或者是>2的偶數則不是素數
for(int i = 1; i <= times; i++) //做times次隨機檢驗
{
long long a = Random( n - 2 ) + 1;
//得到隨機檢驗算子 a
if(!witness( a, n ))
//用a檢驗n是否是素數
return false;
}
return true;
}
int main( )
{
long long tar;
while(cin >> tar)
{
if(miller_rabin( tar )) //檢驗tar是不是素數
cout << "Yes, Prime!" << endl;
else
cout << "No, not prime.." << endl;
}
return 0;
}
部分參考:https://www.cnblogs.com/fzl194/p/9046117.html
