素數判斷和大合數分解_C004素數的判定作品展示
技術標籤:素數判斷和大合數分解
C004素數的判定作品展示1號作品 作者:常晨龍
解題思路:若一個數i不能被2~sqrt(i)間任意一個整數整除,那麼i就為質數。
程式程式碼:
#include#includeint main(){ int f,i,j,t=0; for(i=2;;i++) //從2開始依次判斷每個整數是不是質數 { f=sqrt((float)i);//VC++2010重構多個sqrt,選其一 for(j=2;j<=f;j++) if(i%j==0) break; if(j>=f+1) t++; //用於計數,當前為第t個素數 if(t==2019) break; } printf("%d",i); return(0);}
執行結果:17569
2號作品作者:網安興國
解題思路:本題目主要運用了常見的三種方法來做,其實後面兩種篩選法在這道題上優勢不大,僅供大家交流學習。
/*由素數定理可知,不超過x的素數的個數為x/lnx,由此大致估計,第2019個素數的大小10000-100000之間*/
#include
#include
#include
using namespace std;
const int maxn = 100000;
bool IsPrime_2[maxn];
bool IsPrime_3[maxn];
int prime[2050];
bool IsPrime(int n) //方法一:判斷n是否為素數,樸素做法,後面要呼叫
{
inti,k;
k= sqrt(n);
for(i=2;i<=k;i++)
if(n % i == 0)
break;
if(i> k)
return true;
returnfalse;
}
/*方法二:埃拉斯托特尼篩選法求素數,其原理是大於等於2的數的n(n>=2)倍一定是合數,因為每一個素數都只能被1和本身除盡,
所以,能夠被這些數篩掉的一定是合數,這樣,留下來的一定是素數。所以我們只需要從2開始,將所有2的倍數(>=2倍)標記為合數,
然後從3開始,將3的倍數(>=2倍)標記為合數,以此類推……當然,對於每個數來說篩選的邊界是maxn(這道題是100000以內)
*/
//方法二:埃拉斯托特尼篩選法求素數,時間複雜度為O(n*ln(n))
//注:本方法的時間複雜度的證明需要用到尤拉推出的關於p級數(p=1時)的求和公式,在此就不做詳盡證明,啊,偉大的尤拉,膜拜。
int Erato() //返回第2019個素數
{
//有一個細節是,每一輪篩選開始的時候,如果該數沒有被標記為false,那麼它一定是素數,因為比它小的數都沒能幹掉它! 所以,能留下來的都是王者!
inti,j,cnt = 0;
memset(IsPrime_2,true,sizeof(IsPrime_2));//將該陣列初始化,全部標記為素數
for(i=2;i<=maxn;i++)
{
if(IsPrime_2[i]) //如果是素數,則計數
cnt++;
if(cnt == 2019) //當得到第2019的數時
returni;
for(j=i*2;j<=maxn;j+=i)
IsPrime_2[j]= false;
}
}
/*方法三:本方法還是篩選,主要利用一個素數基本定理的變形:任何一個合數都可以表示為一個素數和一個合數的乘積,所以,
我們只需要用素數和某一個數的乘積作為篩選標準,且保證每一個合數都是被其最小的素數因子篩掉的,這一點很重要,後面會提到。利用這一點,當篩選的時候,碰到某數n可以整除已經篩出的某素數的時候,本輪篩選便可以停止,因為下一個素數乘n一定可以表示為更小的素數和更大的合數之積,所以就不必重複篩選。這個是重要的減少複雜度的依據,也是本方法的關鍵!每次篩選的時候,都利用已經篩出的素數作為新一輪篩選的基礎。這個篩選要多出一個prime陣列,實際上是以空間換時間。
*/
int Euler() //方法三:尤拉篩選法求素數,時間複雜度為O(n)
{
inti,j,cnt = 0;
memset(IsPrime_3,true,sizeof(IsPrime_3));//將所有數初始化為素數
for(i=2;i<=maxn;i++)
{
if(IsPrime_3[i])
prime[cnt++]= i;
if(cnt == 2019)
returnprime[cnt-1];
for(j=0;j
{
IsPrime_3[i*prime[j]]= false;
if(i% prime[j] == 0) //如果可以整除,則跳出去,前面提到過,這是關鍵之處
break;
}
}
}
int main()
{
inti,cnt = 0;
ios::sync_with_stdio(false);
//方法一:樸素做法,時間複雜度為O(n*根號n),具體證明應該會涉及到p級數及留數定理,此處略(其實是本菜雞太弱。。本學期剛開始學複變函式,還沒學到留數定理。。只知道皮毛,不敢妄言。)
for(i=2;i<=maxn;i++)
{
if(IsPrime(i)) //一個一個判斷,如果是素數,則讓計數器cnt加1
cnt++;
if(cnt == 2019)
{
cout<(樸素做法)第2019個素數為:"<
break;
}
}
//方法二:埃拉斯托特尼篩選法
cout<(埃拉斯托特尼篩選法)第2019個素數為:"<
//方法三:尤拉篩選法
cout<(尤拉篩選法)第2019個素數為:"<
return0;
}
執行結果:17569
編後記
大部分程式設計教材都有素數判定例程,所以本期題目算是一個常規題,難度不大。本次共收到7份作品,除開己經展示的作品以外,還有李飛陽、宋雲肥、荃渡、pzd、張恆等也提供了作品。本次題目只是找出指定的一個素數,並沒必要把所有的數都儲存下來,所以,從程式的空間複雜性來說,樸素篩選法更為簡單。由於篇幅限制,未能一一展示其他作品。在此,感謝所有作者及支援本公眾號的同學。
題目連結:C004素數的判定