素數–一個大於1的自然數,除了1和它本身外,不能整除以其他自然數,因其特殊的性質,被廣泛用於密碼學領域,在程序設計競賽及各大公司的面試中也經常出現,今天和大家分享幾道有關素數的基礎問題。
素性測試
問題描述:給定整數num,判斷num是不是素數。
由素數的定義,我們知道一個素數只能擁有兩個約數,即1和它本身,又因爲一個數的約數不能大於它本身,由此只要我們只要遍歷2到num-1,找到除此之外的約數即可判斷其不爲素數,否則就判斷它是一個素數。這樣遍歷時間複雜度爲O(n)。但是,約數是相互對稱的,即n1是num的約數,則num/n1也是num的約數,這樣我們其實搜索2到sqrt(num)(定義sqrt(num)爲num開根號)即可。這樣時間複雜度爲O(sqrt(n))。
代碼:
#include<iostream>
bool is_prime(int num){
bool result = true;
if (num<2)
{
result = false;
}
for (int i = 2; i*i <= num; i++)
{
if (num % i == 0)
{
result = false;
break;
}
}
return result;
}
int main(){
int num = 0;
printf("請輸入數字:\n");
scanf_s("%d", &num);
if (is_prime(num))
printf("Yes\n");
else
printf("No\n");
system("pause");
return 0;
}
費馬檢測
這同樣是一個判斷素數的方法,費馬檢測是基於費馬小定理的檢測方法,是一種基於概率的檢測方法。
費馬小定理:
假如p是質數,且Gcd(a,p)=1,那麼 a^(p-1) ≡1(mod p)。即:假如a是整數,p是質數,且a,p互質(即兩者只有一個公約數1),那麼a的(p-1)次方除以p的餘數恆等於1,即a^(p-1) % p = 1。
其算法的基本思想爲:
如果p爲奇數,隨機的在2到p-1之間選擇一個數進行檢測,若不滿足上式,則p一定不是素數;若滿足上式,則p有可能是素數。p爲偶數時,除2之外均不是素數。
代碼:
#include<iostream>
/*
採用反覆平方法來計算 a^(p-1) mod p
*/
int cal_fermat(int a, int p){
int n = p-1;
int result = 1;
int base = a;
while(n>0){
if (n&1)
{
result = result*base%p;
}
base= base*base%p;
n>>=1;
}
return result;
}
/*
費馬測試
*/
bool prime_fermat(int num){
if (num == 2){
return true;
}
if (num < 2){
return false;
}
if (!(num % 2)){
return false;
}
int test = 0;
while (test < 2){
test = rand()%num;
}
return 1==cal_fermat(test, num);
}
int main(){
int num = 0;
printf("請輸入數字:\n");
scanf_s("%d", &num);
if (prime_fermat(num))
printf("Yes\n");
else
printf("No\n");
system("pause");
return 0;
}
對於大部分a小於p,該算法是正確的,費馬檢測是一個相對可靠的算法,而且在實現大數判斷素數時可以提供相對高的效率來工作。但是該算法並不是嚴格正確的,事實上存在着這樣一些數,它可以滿足所有的a值但卻不是一個素數,這樣的數我們稱爲Carmichael數。
埃式篩法
問題描述:給定整數n,請問n以內有多少個素數
首先,我們來遍歷2到n的所用整數,並使用布爾類型的數組prime_bool來保存每一個數所對應的位置是否爲素數(在初始化時我們將數組中2到n定義爲true);接下來當我們訪問到一個素數時,將其記錄下來,並把其在n範圍內的倍數全部置爲非素數(一個素數不可能含有除1和它本身之外的約數)。
代碼:
#include<iostream>
#define MAX_N 10000
int prime_num[MAX_N];//記錄素數
bool prime_bool[MAX_N+1];
int prime_count(const int end){
for (int i = 2; i <= end; i++)
{
prime_bool[i] = true;
}
int count = 0;
for (int i = 2; i <= end; i++)
{
if (prime_bool[i])
{
prime_num[count++] = i;
for (int j = i+i; j <= end; j+=i)
{
prime_bool[j] = false;
}
}
}
return count;
}
int main(){
int num = 0;
printf("請輸入數字:\n");
scanf_s("%d", &num);
int c = prime_count(num);
printf("共有%d個素數\n", c);
printf("所有的素數:\n");
for (int i = 0; i < MAX_N; i++)
{
if (prime_num[i] == 0)
break;
if (i % 8 == 0)
printf("\n");
printf("%-8d ", prime_num[i]);
}
printf("\n");
system("pause");
return 0;
}
區間篩法
問題描述:給定區間[l, r],問其中含有多少素數
我們知道,若要判斷r是否爲素數,只需遍歷2到sqrt(r)即可,這樣我們只需在區間[2, sqrt(r)]上進行遍歷,通過埃式篩法將[l, r]範圍內的非素數劃去即可得到[l, r]範圍內的素數。
代碼:
#include<iostream>
#define MAX_N 10000
int prime_num[MAX_N];//記錄素數
bool prime_bool[MAX_N+1];
int prime_segment(int l, int r){
int count = 0;
for (int i = 2; i*i <= r; i++)
{
prime_bool[i] = true;
}
for (int i = l; i <= r; i++)
{
prime_bool[i] = true;
}
for (int i = 2; i*i <= r; i++)
{
if (prime_bool[i])
{
for (int j = i+i; j <= r; j += i)
{
prime_bool[j] = false;
}
}
}
for (int i = l; i <= r; i++)
{
if (prime_bool[i])
{
prime_num[count++] = i;
}
}
return count;
}
int main(){
int l = 0;
printf("請輸入開始數字:\n");
scanf_s("%d", &l);
int r = 0;
printf("請輸入結束數字:\n");
scanf_s("%d", &r);
int c = prime_segment(l, r);
printf("共有%d個素數\n", c);
printf("所有的素數:\n");
for (int i = 0; i < MAX_N; i++)
{
if (prime_num[i] == 0)
break;
if (i % 8 == 0)
printf("\n");
printf("%-8d ", prime_num[i]);
}
printf("\n");
system("pause");
return 0;
}