今天想總結一下素數的幾種求法
暴力方法:
最簡單的方法就是從2開始循環到n,如果沒有一個數能被n整除就是素數
#include<stdio.h>
int isPrime(int n) {
for(int i=2; i<n; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
int main() {
int x ;
scanf("%d", &x) ;
if(isPrime(x))
printf("YES\n") ;
else
printf("NO\n") ;
return 0 ;
}
這個方法是最簡單粗暴的,但是也是效率最低的需要循環到n-1,可以進行一點改進,循環的判斷條件只需要到n/2+1就可以了
int isPrime(int n) {
for(int i=2; i<n/2+1; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
這樣循環的次數還是不夠少,其實只需要判斷到根號n次就行,一般來說可以使用sqrt(n)來求根號n,我們還可以用 i*i<n來實現
int isPrime(int n) {
for(int i=2; i*i<n; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
其實還可以繼續優化這個方法,因爲除了2以外的所有偶數都不可能是素數,所以我們只需要判斷奇數就行
bool isPrime(int n) {
if(n%2==0 && n!=2) //除了2以外的所有偶數都不是素數
return false ;
else if(n == 2)
return true ;
for(int i=3; i*i<n; i+=2) { //3-根號n內沒有一個數能被n整除就是素數
if(n%i == 0)
return false ;
}
return true ;
}
這是我目前能想到用暴力的方法最優的樣子,如果還有可以優化的地方歡迎指教
埃氏篩:
這個方法是通過打表找出指定範圍內所有的素數,當數字範圍小的時候用暴力沒問題但只要範圍太大就用不了了,就可以用這個方法
原理:從2開始循環,將2存入素數表中,把2所有的倍數都捨去,然後對3進行相同的操作,一直這樣循環到頭
#include<cstdio>
int isPrime[100000] ; //記錄該點是否是素數
int prime[100000] ; //存素數
int main() {
int count = 0 ; //記錄共有幾個素數
for(int i=2; i<100000; i++) {
if(isPrime[i] == 0) { //從2 開始將2的所有倍數除去然後是3的倍數4的倍數…………
prime[count++] = i ; //將素數存入
for(int j=i*i; j<100000; j+=i)
isPrime[j] = 1 ;
}
}
for(int i=0; i<count; i++) {
printf("%d ",prime[i]) ;
if(i%10 == 0)
printf("\n") ;
}
return 0 ;
}
第二層for循環中j 從 i * i 而不是從 i + i開始,因爲 i*(2~ i-1)在 2~i-1時都已經被篩去,所以從i * i開始
這個方法有個缺點就是會重複多次篩去一個點,於是就有了下一個方法
歐拉篩:
這個方法是在埃氏篩的基礎上做了一點改進,原理是把一個合數(6=2*3)通過它的最小質因子篩去
#include<cstdio>
int isPrime[100000] ;
int prime[100000] ;
int main() {
int count = 0 ;
for(int i=2; i<100000; i++) {
if(isPrime[i] == 0)
prime[count++] = i ;
for(int j=0; j<count&&i*prime[j]<100000; j++) { //找出由i和已得到的素數相乘得到的數
isPrime[i*prime[j]] = 1 ; //將由i和最小質因數相乘得到的數排除,
if(i%prime[j] == 0) //如果i能被已經存過的素數整除,說明在之前就由其他的i和最小質因數相乘過,不用重複查
break ;
}
}
for(int i=0; i<count; i++) {
printf("%d ",prime[i]) ;
if(i%10 == 0)
printf("\n") ;
}
return 0 ;
}
總結:
通過寫這個求素數的不同方法,從低效的逐漸提高效率,也反應了學習編程一年的一些想法上的進步,這樣看上去還是比較開心的。所以說考慮一個問題還是要多研究多考慮深一些,不斷優化自己的代碼