ACM數論之旅1---素數(萬事開頭難(>_<))


前言:好多學ACM的人都在問我數論的知識(其實我本人分不清數學和數論有什么區別,反正以后有關數學的知識我都扔進數論分類里面好了)

於是我就准備寫一個長篇集,把我知道的數論知識和ACM模板都發上來(而且一旦模板有更新,我就直接在博客上改了,所以記得常來看看(。・ω・))

 

 

廢話說完了,直接進入正題ヾ(=^▽^=)ノ

 

 

 

素數,又叫質數,定義是除了1和它本身以外不再有其他的因數

我們通過這個定義,可以寫如下程序判斷一個數是不是質數

1 bool prime(int x){//判斷x是不是質數,是返回true,不是返回false 
2     if(x <= 1) return false; 
3     for(int i = 2; i < x; i ++){
4         if(x % i == 0) return false;
5     }
6     return true;
7 }

 

這個程序的時間復雜度是O(n),有沒有更快的方法,當然

看這個

 1 bool prime(int x){//判斷x是不是質數,是返回true,不是返回false 
 2     if(x <= 1) return false; 
 3     for(int i = 2; i <= sqrt(x + 0.5); i ++){//0.5是防止根號的精度誤差 
 4         if(x % i == 0) return false;
 5     }
 6     return true;
 7 }
 8 //另一種方法,不需要根號 
 9 bool prime(int x){//判斷x是不是質數,是返回true,不是返回false 
10     if(x <= 1) return false; 
11     for(int i = 2; i * i <= x; i ++){//用乘法避免根號的精度誤差 
12         if(x % i == 0) return false;
13     }
14     return true;
15 }
16 //根據題目不同,如果i*i會爆int,記得開longlong 

 

這個復雜度是O(√n),速度快多了(#°Д°)

 

 

 

 

 

 

 

 

根據題目不同,有可能你需要先預處理出1~N這N個數是否是素數

如果用剛剛的方法,復雜度就是O(n√n)

 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 bool prime[N];
 4 bool is_prime(int x){
 5     if(x <= 1) return false; 
 6     for(int i = 2; i * i <= x; i ++){
 7         if(x % i == 0) return false;
 8     }
 9     return true;
10 }
11 void init(){
12     for(int i = 0; i < N; i ++){
13         prime[i] = is_prime(i);
14     }
15 }
16 int main(){
17     init();
18 }

 

 

如果n大一點,就太慢了(。・ω・)ノ゙

 

介紹一種新方法,埃篩

 

埃篩--------------埃拉托斯特尼篩法,或者叫埃氏篩法

 

原理:如果找到一個質數,那么這個質數的倍數都不是質數

 

比如2是質數,那么4,6,8,10,12...都不是質數

然后看3是質數,那么6,9,12,15,18,21...都不是質數

然后看4,4已經被2標記為合數了,所以跳過

然后看5......這樣一直篩下去

 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 bool prime[N];
 4 void init(){
 5     for(int i = 2; i < N; i ++) prime[i] = true;//先全部初始化為質數 
 6     for(int i = 2; i < N; i ++){
 7         if(prime[i]){//如果i是質數 
 8             for(int j = 2*i; j < N; j += i){//從i的兩倍開始的所有倍數 
 9                 prime[j] = false; 
10             }
11         }
12     }
13 }
14 int main(){
15     init();
16 }

 

因為一些數字,比如6既被2的for循環經過又被3的for循環經過,所以復雜度不是O(n)

這個復雜度經過專業人士檢驗,復雜度O(nloglogn)(學過高數的小朋友可以自己證明≖‿≖✧當然也可以去百度)

 

知道原理后,我們再稍微優化一下就更快了

 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 bool prime[N];
 4 void init(){
 5     for(int i = 2; i < N; i ++) prime[i] = true;
 6     for(int i = 2; i*i < N; i ++){//判斷改成i*i<N 
 7         if(prime[i]){
 8             for(int j = i*i; j < N; j += i){//從i*i開始就可以了 
 9                 prime[j] = false;  
10             }
11         }
12     }
13 }
14 int main(){
15     init();
16 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

好戲都是要留到最后的≖‿≖✧確實還有O(n)的做法

這個算法名字叫線篩

 

 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 bool prime[N];//prime[i]表示i是不是質數 
 4 int p[N], tot;//p[N]用來存質數 
 5 void init(){
 6     for(int i = 2; i < N; i ++) prime[i] = true;//初始化為質數 
 7     for(int i = 2; i < N; i++){
 8         if(prime[i]) p[tot ++] = i;//把質數存起來 
 9         for(int j = 0; j < tot && i * p[j] < N; j++){
10             prime[i * p[j]] = false;
11             if(i % p[j] == 0) break;//保證每個合數被它最小的質因數篩去 
12         }
13     }    
14 }
15 int main(){
16     init();
17 }

 

 

這個方法可以保證每個合數都被它最小的質因數篩去

所以一個數只會經過一次

時間復雜度為O(n)

 

其實loglogn非常小,把埃篩看成線性也無妨,畢竟它比線篩好寫

 

 

 

 

 

 

 

 

 

 

基於埃篩的原理,我們可以用它干很多事

比如預處理每個數的所有質因數

 1 #include<cstdio>
 2 #include<vector>
 3 using namespace std;
 4 const int N = 100000 + 5;
 5 vector<int > prime_factor[N];
 6 void init(){
 7     for(int i = 2; i < N; i ++){
 8         if(prime_factor[i].size() == 0){//如果i是質數 
 9             for(int j = i; j < N; j += i){
10                 prime_factor[j].push_back(i); 
11             }
12         }
13     }
14 }
15 int main(){
16     init();
17 }
View Code

比如預處理每個數的所有因數

 1 #include<cstdio>
 2 #include<vector>
 3 using namespace std;
 4 const int N = 100000 + 5;
 5 vector<int > factor[N];
 6 void init(){
 7     for(int i = 2; i < N; i ++){ 
 8         for(int j = i; j < N; j += i){
 9             factor[j].push_back(i); 
10         }
11     }
12 }
13 int main(){
14     init();
15 }
View Code

比如預處理每個數的質因數分解

 1 #include<cstdio>
 2 #include<vector>
 3 using namespace std;
 4 const int N = 100000 + 5;
 5 vector<int > prime_factor[N];
 6 void init(){
 7     int temp;
 8     for(int i = 2; i < N; i ++){
 9         if(prime_factor[i].size() == 0){
10             for(int j = i; j < N; j += i){
11                 temp = j;
12                 while(temp % i == 0){
13                     prime_factor[j].push_back(i);
14                     temp /= i;
15                 }  
16             }
17         }
18     }
19 }
20 int main(){
21     init();
22 }
View Code

 

 

 

 

 

 

 

 

 

世界之大無奇不有(。-`ω´-)數論是個可怕的東西

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM