當只需要求某個數是不是素數的時候,我們可以直接通過素數的定義來求,即如果可以被除1及素數本身的其他數整除,則這個數不是素數
但是如果要求某個范圍內的素數的個數的時候這個方法就不太合適了。雖然我們可以進行預處理,但是這種方法比較慢,一旦范圍過大,預處理過程便會超時。
因此,需要使用篩法求素數,這樣可以在線性時間內求得范圍內每個數是否為素數。
思想:去除要求范圍內所有的合數,剩下的就是素數,而任何合數都可以表示為素數的乘積,因此,如果已知一個數為素數,則它的倍數都是合數
普通的線性篩法:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define ll long long 6 const int MAX = 1000100; // 求MAX范圍內的素數 7 ll su[MAX],cnt; 8 bool isprimer[MAX]; 9 void prime() 10 { 11 cnt = 1; 12 memset(isprimer,1,sizeof isprimer); // 初始化認為所有數都為素數 13 isprimer[0] = isprimer[1] = 0; // 0和1不是素數 14 for(ll i = 2;i <= MAX;i++) 15 { 16 if(isprimer[i]) // 保存素數 17 { 18 su[cnt++] = i; 19 } 20 for(ll j = i*2;j <= MAX;j += i) // 素數的倍數都為合數 21 { 22 isprimer[j] = 0; 23 } 24 } 25 } 26 27 int main() 28 { 29 prime(); 30 for(ll i = 1;i < cnt;i++) 31 printf("%lld ",su[i]); 32 return 0; 33 }
弊端:雖然這種方法已經極大的減小了判斷的時間,但是仍然有一些重復計算。比如判斷2的時候將(2*3)6篩了一遍。
在判斷3的時候又將6(3*2)篩了一遍。
如果只篩選小於等於i的素數與i的乘積,便可以盡量減少重復的篩選,也不會遺漏掉,大大提高了計算效率
優化后的線性篩法:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define ll long long 6 const int MAX = 1000100; // 求MAX范圍內的素數 7 ll su[MAX],cnt; 8 bool isprimer[MAX]; 9 void prime() 10 { 11 cnt = 1; 12 memset(isprimer,1,sizeof isprimer); // 初始化認為所有數都為素數 13 isprimer[0] = isprimer[1] = 0; // 0和1不是素數 14 for(ll i = 2;i <= MAX;i++) 15 { 16 if(isprimer[i]) // 保存素數 17 { 18 su[cnt++] = i; 19 } 20 for(ll j = 1;j < cnt && su[j]*i < MAX;++j) // 素數的倍數都為合數 21 { 22 isprimer[su[j]*i] = 0; // 篩掉小於等於i的素數和i的積構成的合數 23 } 24 } 25 } 26 27 int main() 28 { 29 prime(); 30 for(ll i = 1;i < cnt;i++) 31 printf("%lld ",su[i]); 32 return 0; 33 }
