線性篩素數(歐拉篩)


線性篩是一個很基礎的算法,但是我一直沒學。直到一次考試,因為O(n√n)會超時,用了表篩,結果被卡了代碼長度,於是開始學習歐拉篩。

算法思路:

  對於每一個數(無論質數合數)x,篩掉所有小於x最小質因子的質數乘以x的數。比如對於77,它分解質因數是7*11,那么篩掉所有小於7的質數*77,篩掉2*77、3*77、5*77。

  好吧,是不是聽起來太簡單了。。。。沒事,重點在證明。

算法證明:

  首先我們要明確證明思路。如果要證明它是對的,只要保證兩點:沒用重復篩、沒有漏篩

  1、沒有重復篩。

    我們假設一個合數分解成p1*p2*p3,並且保證p1<p2<p3。我們知道,篩掉這個合數的機會有:p1和p2*p3,p2和p1*p3,p3和p1*p2。但我們知道,我們選擇的那個質數必須小於那個合數的最小質因子。比如p2和p1*p3,因為p2>p1,所以這樣是篩不到的。唯一會篩的是第一種:p1和p2*p3。

  2、沒有漏篩。

  還是假設把這個合數分解質因數a*b*c,保證a<b<c然后我們設s=b*c,s肯定小於剛才那個合數,說明肯定對於它已經篩過,然后a肯定小於s,因為s=b*c,並且a是最小的因子。說明a*s也就是這個合數一定篩過。

證明沒看懂的直接看代碼吧。。挺好背的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#define in(a) a=read()
#define REP(i,k,n) for(int i=k;i<=n;i++)
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            f=-1;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return x*f;
}
int prime[1000010],book[1000010];
int n,ind;
int main(){//計算1~n的素數
    in(n);
    REP(i,2,n){
        if(!book[i])  prime[++ind]=i;//如果沒有篩過,記錄素數
        REP(j,1,ind){//其中記錄數組里的素數保證嚴格遞增
            if(i*prime[j]>n)  break;//保證小於n,要不然沒有意義
            book[i*prime[j]]=1;//篩去這個合數
            if(!i%prime[j])  break;//如果>=這個數的最小質因子,那就結束
        }
    }
    REP(i,1,ind)  printf("%d ",prime[i]);//輸出
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM