基礎數學知識·質數
初見安~本章講一點兒數學知識最近講的東西好像越來越基礎了……
1.質數
質數——若一個正整數除了它自己和1沒有別的因數了,則稱這個數為質數(or素數),否則合數。
比如:3=1*3,3就是一個質數;8=1*8=2*4,8就是個合數。
在c++裡我們要判斷質數or篩選質數,可以運用到以下幾種方法:
(1) 試除法
若一個正整數N為合數,則在2~N-1裡必定可以找到N的因數M。而我們可以知道:N的合數成雙出現,不成雙則為√N,如9=3*3(這裡算3為一個合數),所以N若為合數,則在2~√N裡一定可以找到一個N的因數。因此我們可以直接用for迴圈除以N來驗證N是否為質數。
#include<iostream> #include<cmath> using namespace std; int main() { int n; cin>>n; for(int i=2;i<=sqrt(n);i++) { if(n%i==0)//取餘 { cout<<"No"<<endl; return 0;//找到了一個因數,則不必繼續了 } } cout<<"Yes"<<endl; return 0; }
(2)Eratosthenes演算法
看起來是個很高大上的名字(我都不會讀),其實也是很簡單的——但這一演算法比較適用於要打出2~N之內的質數or統計有多少個,也就是全域性性的問題。同樣基於我們對質數的定義——一個正整數,除了1和它本身沒有別的因數。所以有別的因數的都不是質數。
例——輸出1~N以內所有的質數。
#include<iostream> using namespace std; int n; bool v[110]; int main() { cin>>n; for(int i=2;i<=n;i++)//2~n { if(v[i]) continue;//已經標記為合數了,繼續迴圈不做操作 cout<<i<<" "; for(int j=i;j<=n/i;j++) { v[i*j]=1;//找可以被i整除的n以內的數並標記 } } return 0; }
這一演算法的時間複雜度為O(NloglogN),效率是十分高的,也十分常用。
(3)線性篩法
Eratosthenes演算法就算優化也會重複標記合數,如12會被2和3重複標記,所以我們試試線性篩法。
這一篩法運用到了一個數學結論:任何一個合數都可以被分解為幾個素數的乘積。如44=2*2*11等等。
考慮到上述的重複標記問題(加一個v的判斷也可以,但為了更優化還是介紹一下)
【p.s】最小質因數,顧名思義就是某合數的一個最小的為質數的因數。
#include<iostream> using namespace std; int n; int v[1100];//在i下標處放i的最小質因數 int prime[1100];//存放質數 int m=0; int main() { cin>>n; for(int i=2;i<=n;i++) { if(v[i]==0)//不曾被標記上最小質因數,則i為質數。 { v[i]=i; //它本身作為一個因數,去篩將它作為最小質因數的數 prime[++m]=i; } for(int j=1;j<=m;j++) { if(prime[j]>v[i]||prime[j]>n/i) break; //i有比prime[j]更小的質因數,也就是已被標記 v[i*prime[j]]=prime[j];//沒有則繼續標記 } } for(int i=1;i<=m;i++) { cout<<prime[i]<<" "; } return 0; }
這一演算法中每個合數只會被它的最小質因數標記一次,時間複雜度為O(N)。
(4)質因數分解
例題:輸入一大於一的整數n,將n分解為幾個質數的乘積。
由上文所提到的:每個合數都可以被分解為幾個質數的乘積,同時任意一個大於1的正整數都能唯一分解為有限個質數的乘積。所以我們可以嘗試一下將試除法和Eratosthenes演算法結合起來——掃描2~√N中的每個數d,若d能整除N,則存起來,並統計需要多少個d相乘。這裡d天然保證了為質數,因為每個合數都可以分解為幾個質數的乘積,在到這個合數之前n就應當已經被除盡了。
#include<iostream>
#include<cmath>
using namespace std;
int n;
int p[1100];//從小到大存放有哪些因數
int c[1100];//存放第m個因數需要乘的次數
int m=0;//計數
int main()
{
cin>>n;
cout<<n;//格式而已,不用在意。畢竟操作過後n變了
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0)//找到因數
{
p[++m]=i;
c[m]=0;
while(n%i==0)//可以分解i則繼續分解
{
n/=i;
c[m]++;//統計次數
}
}
}
if(n>1)//最後n還是無法分解完,則為質數
{
p[++m]=n;c[m]=1;
}
cout<<"=";//作為一個強迫症,為了格式而已……
int num=0;//為了格式+1
for(int i=1;i<=m;i++)
{
for(int j=1;j<=c[i];j++)
{
if(num==0) cout<<p[i];
else cout<<"×"<<p[i];
num++;
}
}
return 0;
}
畢竟基本上是純數學知識,還是很好理解的~
迎評:)
——End——