1. 程式人生 > >基礎數學知識·質數

基礎數學知識·質數

初見安~本章講一點兒數學知識最近講的東西好像越來越基礎了……

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——