1. 程式人生 > 其它 >素數篩

素數篩

技術標籤:演算法進階指南演算法

埃氏篩

 &emap;簡單來說就是從質因子從小到大擴散,篩選出合數,最終剩餘的數就是質數;
  有一點優化就是對於小於x^2的來說,由於從2到x-1已經被篩選過,所以此時小於x^2的x的倍數一定已經被篩到過,所以可以跳過;
  埃氏篩已經是一個比較優秀的演算法了,其時間複雜度是接近線性時間複雜度,大多數程式競賽問題也可以用埃氏篩解決;

code

int vise[1000001];
void prime(int n){
	memset(vise,0,sizeof(vise));
	for(int i=2;i<=n;i++){
		if(vise[i])
continue; cout<<i<<" "; for(int j=i*i;j<=n;j+=i)vise[j]=1;//可以直接從i^2開始遍歷 } cout<<endl; return ; }

線性篩

  儘管埃氏篩已經非常優秀了,但是埃氏篩還是會重複遍歷某些元素,這是因為沒有指定每一個合數唯一的生成序,由於每個合數都可以進行質因子分解,那麼我們只需要按照質因子從大到小的順序生成每一個元素,那麼每一個數都只會被生成一次,而不會是多次,具體來說我們儲存每個已經被篩選到的數的最小的質因子,當某個數為質數時,最小質因子就是該數;

  這樣問題就變成了我們如何記錄每個合數的最小質因子;假設我們已經知道某個合數的最小質因子為vise[i],那麼所有小於vise[i]的質因子與i的乘積生成的數的最小質因子就確定了,我們可以得到vise[i*p]=p,其中p是不超過vise[i]的質因子,那麼此時我們就可以保證生成每一個合數時都是按照質因子從大到小的順序生成的;

code

int vise[100001];
void primes(int n){
	//指定一個生成順序,從而避免重複篩選
	memset(vise,0,sizeof(vise));
	vector<int> arr;
	for(int i=2;i<=n;i++
){ if(vise[i]==0){ vise[i]=i; arr.push_back(i); } for(int j=0;j<arr.size()&&arr[j]<=vise[i]&&i*arr[j]<=n;j++){ vise[i*arr[j]]=arr[j]; } } for(int i=0;i<arr.size();i++)cout<<arr[i]<<" ";cout<<endl; return; }