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

前言:好多學ACM的人都在問我數論的知識(其實我本人分不清數學和數論有什麼區別,反正之後有關數學的知識我都扔進數論分類裏面好了)算法

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

 

 

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

 

 

 

素數,又叫質數,定義是除了1和它自己之外再也不有其餘的因數spa

咱們經過這個定義,能夠寫以下程序判斷一個數是否是質數3d

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),有沒有更快的方法,固然code

看這個blog

 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)it

 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

 

 

 

 

 

 

 

 

 

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

相關文章
相關標籤/搜索