實驗4-3 循環結構-判素數
編寫程序sushu.c,輸入一個正整數n(n>2),判斷n是否爲素數。
格式要求 輸入:scanf("%d",&n) 輸出: (1)如果n<=2,則printf(“ERROR”)
(2)如果是素數,則printf("%d是素數", n) 否則printf("%d不是素數", n)保存,編譯、運行、測試成功後將源程序文件(.c或.cpp)壓縮,提交。
方法一 樸素做法
時間複雜度:O(n)
直接一個一個篩
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
if(n<=2)
{
printf("ERROR");
}
else
{
bool is_prime=true;
for(int i=2;i<n;i++)
{
if(n%i==0)
{
is_prime=false;
}
}
if(is_prime)
{
printf("%d是素數",n);
}
else
{
printf("%d不是素數",n);
}
}
return 0;
}
方法二:方法一改進
時間複雜度:O(根號n)
如果是偶數,且不是2,肯定不是素數
想象因式分解。3*4=12,不必要從2一直篩到n-1,直接篩到即可
這裏最好不要用sqrt函數,計算機中,平方運算所需cpu週期比根號少得多,故更快些
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
if(n<=2)
{
printf("ERROR");
}
else if(n%2==0&&n!=2)
{
printf("%d不是素數",n);
}
else
{
bool is_prime=true;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
is_prime=false;
}
}
if(is_prime)
{
printf("%d是素數",n);
}
else
{
printf("%d不是素數",n);
}
}
return 0;
}
方法三:6倍數判別法
時間複雜度:O(根號n)是鬆的時間複雜度,實際上比上一個快2-4倍
這裏有一個數學定理:大於等於5的質素一定和6的倍數相鄰
詳細解答請轉步知乎
如何證明大於等於5的質素一定和6的倍數相鄰?
其實就是孿生質數,有興趣的讀者可以去網上搜索瞭解
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
if(n<=2)
{
printf("ERROR");
}
else if(n%2==0&&n!=2)
{
printf("%d不是素數",n);
}
else
{
int i,is_prime=1;
if(n%6!=1&&n%6!=5)
{
is_prime=0;
}
else
{
for(i=5;i*i<=n;i+=6)
{
if(n%i==0||n%(i+2)==0)
{
is_prime=0;
}
}
}
if(is_prime)
{
printf("%d是素數",n);
}
else
{
printf("%d不是素數",n);
}
}
return 0;
}
方法四:Miller-Rabin素性測試
採用了隨機抽樣測試的方法,實際上有可能會判不準,因爲費馬小定理的逆命題實際上是錯的,但是其發生概率實際很低
在測試質數時,抽樣法是一個非常有用的工具。下面給出一種質數判定方法:
對於待判定的整數n。設n-1=d×2s(d是奇數)。對於給定的基底a,若ad≡1 (mod n),或存在0≤r<s使a≡-1 (mod
n),則稱n爲以a爲底的強僞質數。利用二分法,可以在O(logn)的時間內判定n是否爲以a爲底的強僞質數。
對於合數c,以小於c的數爲底,c至多有1/4的機會爲強僞質數。如果不是隨機抽樣,而是抽樣特殊情況——最小的幾個質數,則: 如果只用2一個數進行測試,最小的強僞質數(反例)是2047,所以一個數顯然不夠;
如果用2和3兩個數進行測試,最小的強僞質數爲1373653,大於106;
如果用2,3,5進行測試,最小的強僞質數爲25326001,大於2×107;
如果用2,3,5,7進行測試,最小的強僞質數爲3215031751,大於3×109,已經比32位帶符號整數的最大值還大了。
可見,通常只要抽取2,3,5,7這幾個固定的數進行測試就能保證測試的正確性了。
適用於大數素性判斷,用到了費馬小定理
感興趣的讀者可以看以下兩篇文章
Miller-Rabin素性測試算法詳解
素數與素性測試(Miller-Rabin測試
這在ACM中有可能會遇到,殺雞焉用牛刀,上面三種交學校的實驗夠用了
這裏用C語言實現
#include<stdio.h>
typedef long long ll;
ll pow_mod(ll a,ll b,ll r)
{
ll ans=1,buff=a;
while(b)
{
if(b&1)
{
ans=(ans*buff)%r;
}
buff=(buff*buff)%r;
b>>=1;
}
return ans;
}
ll test(ll n,ll a,ll d)
{
if(n==2)
{
return 1;
}
if(n==a)
{
return 0;
}
if(d%2==0)
{
d>>=1;
}
int t=pow_mod(a,d,n);
while(d!=n-1&&t!=n-1&&t!=1)
{
t=t*t%n;
d<<=1;
}
return t==n-1||(d&1)==1;
}
ll isprime(ll n)
{
int a[]={2,3,5,7};
for(int i=0;i<=3;i++)
{
if(n==a[i])
{
return 1;
}
if(!test(n,a[i],n-1))
{
return 0;
}
}
}
int main()
{
int n;
scanf("%d",&n);
if(n<=2)
{
printf("ERROR");
}
else if(n%2==0&&n!=2)
{
printf("%d不是素數",n);
}
else
{
if(isprime(n))
{
printf("%d是素數",n);
}
else
{
printf("%d不是素數",n);
}
}
return 0;
}