算法刷题-数论-质数的判定、分解质因数、筛质数
文章目錄
- 數論
- 1. 質數
- 質數的判定---試除法
- 分解質因數---試除法
- 篩質數
- 樸素篩法
- 埃氏篩法
- 線性篩法
數論
1. 質數
質數:在大于1的整數中,如果只包含1和它本身這兩個約數,那么這個數就稱為質數。
判斷質數最暴力的寫法,按照質數的定義:看是否有其他的因子。
最樸素的暴力的時間復雜度O(n)
//時間復雜度O(n) bool isPrime(int n){if(n<2) return false;for(int i=2;i<n;i++)if(n%i==0) return false;return true;}質數的判定—試除法
題目鏈接:Acwing866. 試除法判定質數
優化:枚舉到n\sqrt{n}n?,因為因數都是成對出現的,
如果d是n的因子,那么n/d也是n的因子,所以只需要枚舉一部分就行,枚舉哪一部分呢?就是d≤n/d 推出,d≤nd≤\sqrt{n}d≤n?
這里n\sqrt{n}n?的寫法需要注意,這種調用數學函數n\sqrt{n}n?的寫法不好,太慢;另外寫成i?i≤ni*i≤ni?i≤n存在溢出風險,最好的寫法是i≤n/ii≤n/ii≤n/i
試除法求質數的時間復雜度O(n\sqrt{n}n?)
//優化成O(根號n) bool isPrime(int n){if(n<2) return false;for(int i=2;i<=n/i;i++)if(n%i==0) return false;return true;}分解質因數—試除法
題目鏈接:Acwing867. 分解質因數
暴力求質因數
下面i就是質因子,s是質因子i的階數。
暴力的時間復雜度O(n)
void divide(int n){for(int i=2;i<=n;i++){if(n%i==0){ //i一定是質數,因為下面n/=i,n一直在更新,如果是合數的話,已經被2除干凈了int s=0;//統計質因子的階數while(n%i==0){s++;n/=i;}cout<<i<<" "<<s<<endl;}} }優化
給出一個重要性質:正整數n的因子中,最多包含1個大于n\sqrt{n}n?的質因子。(可以用反證法來證明)
這里試除法的優化就是枚舉到n\sqrt{n}n?,然后剩下的1個大于n\sqrt{n}n?的質因子單獨處理,這樣試除法分解質因數的時間復雜度O(n\sqrt{n}n?)
ac代碼
#include<bits/stdc++.h> using namespace std;//試除法分解質因數 void divide(int n){//處理≤sqrt(n)的質因子for(int i=2;i<=n/i;i++){if(n%i==0){int s=0;while(n%i==0){s++;n/=i;}cout<<i<<" "<<s<<endl;}}//單獨處理大于sqrt(n)的質因子if(n>1) cout<<n<<" "<<1<<endl;}int main(){int n;cin>>n;int tmp;for(int i=0;i<n;i++){cin>>tmp;divide(tmp);cout<<endl;}}篩質數
題目鏈接acwing868. 篩質數
樸素篩法
樸素篩法的思想:從小到大枚舉所有數,每次刪掉該數的所有倍數,比如枚舉到2,就刪掉所有2的倍數。枚舉到3,就刪掉所有3的倍數。
需要用到prime數組,用來存放所有的質數;st數組,用來記錄是否是某個數的倍數,即判斷哪些是質數。需要count1變量,來存儲質數的個數。
遍歷的時候,需要考慮等號取不取。
樸素篩法的時間復雜度O(n×logn)O(n\times logn)O(n×logn)
#include<bits/stdc++.h> using namespace std;const int maxn=1000010;int prime[maxn];//prime數組存的是從小到大的質數 int count1=0; //質數的個數 bool st[maxn]; //判斷是否是質數//樸素的篩法 void primeNumber(int n){for(int i=2;i<=n;i++){if(!st[i]){ //i不是別人的倍數,那么就是質數prime[count1++]=i;}for(int j=i+i;j<=n;j+=i) st[j]=true;//倍數篩掉}cout<<count1<<endl;}int main(){int n;cin>>n;primeNumber(n);return 0;}埃氏篩法
優化:不需要篩掉所有數的倍數(合數的倍數一定不是質數,不用管),只需要篩掉質數的倍數,(因為質數的倍數是合數)。
//優化的篩法 void primeNumber1(int n){for(int i=2;i<=n;i++){if(!st[i]){ //i不是別人的倍數,那么就是質數prime[count1++]=i;for(int j=i+i;j<=n;j+=i) st[j]=true;//質數的倍數篩掉} }cout<<count1<<endl;}有質數定理:當n很大時,1~n中有nlnn\frac{n}{lnn}lnnn?個質數。
優化的篩法(埃氏篩法)時間復雜度O(n×loglogn)O(n\times loglogn)O(n×loglogn),可以粗略地看成是O(n)。
兩次提交結果
線性篩法
先說效率,數量級在10^7時候,線性篩法比埃氏篩法快一倍大概。
線性篩法時間復雜度O(n)
線性篩法的核心:一個數k只會被k的最小質因子篩掉。
//線性篩法void primeNumber2(int n){for(int i=2;i<=n;i++){if(!st[i]) prime[count1++]=i;for(int j=0;prime[j]<=n/i;j++){st[prime[j]*i]=true;if(i % prime[j]==0) break; //prime[j]一定是i的最小質因子}}cout<<count1<<endl; }補充
如何找到第一個大于100000的質數呢?
解答:就是使用試除法判斷質數的方法,從100000開始遍歷,使用標志flag,如果i不是質數,flag=false;如果i是質數,flag=true;然后判斷flag如果等于true,就break跳出循環,輸出i,即為大于100000的最小質數。
#include<bits/stdc++.h> using namespace std;const int maxn=100000;int main(){for(int i=maxn;;i++){bool flag=true;for(int j=2;j<=i/j;j++){if(i%j==0){flag=false;break;}}if(flag){cout<<i<<endl;break;}}} 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的算法刷题-数论-质数的判定、分解质因数、筛质数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Leetcode1706. 球会落何处[
- 下一篇: C++11语言新特性-《C++标准库(第