prime sieve 素数筛

index > Algebra > prime sieve


主要内容

本篇只收录较快的线性筛法,作用就是求2到n的所有素数,顺便得到判断数组。

单纯求素数,标记所有合数。(常用)

const int MAXN = -1;//10000005
int prime[MAXN],pnum;
bool is_composite[MAXN];

void sieve(const int &n) {
    
    
  	// 1 is exception
    //clock_t begin = clock();
    for (int i = 2; i < n; ++i) {
    
    
        if (!is_composite[i]) prime[++pnum]=i;
        for (int j = 1; j <=pnum  && i * prime[j] < n; ++j) {
    
    
            is_composite[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
    //cout << (double) (clock() - begin) / CLOCKS_PER_SEC << endl;
}

求素数的同时,求所有合数最小的质因子。

const int MAXN = -1;//10000005
int prime[MAXN], pnum;
int min_composite[MAXN];

void sieve(const int &n) {
    
    
  	// 1 is exception
    //clock_t begin = clock();
    for (int i = 2; i < n; ++i) {
    
    
        if (!min_composite[i]) {
    
    

            prime[++pnum] = i;
            min_composite[i] = i;
        }
        for (int j = 1; j <= pnum && prime[j] <= min_composite[i] && i * prime[j] < n; ++j) {
    
    
            min_composite[i * prime[j]] = prime[j];
//            if (i % prime[j] == 0) break;
        }
    }
    //cout << (double) (clock() - begin) / CLOCKS_PER_SEC << endl;
}

&& prime[j] <= min_composite[i]if (i % prime[j] == 0) break;相差无几,任选其一即可。

理解

Sieve of Eratosthenes 的思路很好理解,每次装入一个素数就把所有这个数的倍数都划掉。但是容易发现,很多数会被多次划掉。时间复杂度是 O ( n l o g l o g n ) O(n log log n ) O(nloglogn)的,不太理想。

Euler’s sieve 的角度其实不太一样(尽管两者代码很相似)。为了理解,最好优先看第二份代码。下面是我的个人理解。

既然不希望合数被重复删去,那么应该尽量让所有合数是被最小素因子去掉的。这就构成了一种转移。假设 p i pi pi i i i的最小素因子,那么对于所有小于等于 p i pi pi的素数 p j pj pj有, p j ∗ i pj*i pji是一个合数,并且以 p j pj pj为最小素因子。

当枚举到 i i i的时候,已经有了不超过 i i i的所有最小素数,那就可以通过遍历 i ∗ p r i m e [ j ] i * prime_{[j]} iprime[j] 依此把后面的数给删去。

被删去的状态,是从最小素数集合 p r i m e [ j ] prime_{[j]} prime[j]中转移过来的,所以是可以理解新的合数是被最小的素因子删去了。

但不能一直删,当$ i % prime[j] == 0$ 成立,说明此时的 p r i m e [ j ] prime[j] prime[j]恰好是$ i 的 最 小 素 数 , 的最小素数, i * prime_{[j+1]} 之 后 的 倍 数 , 应 该 由 之后的倍数,应该由 i$作为最小素因子时删去。

$ prime[j] <= min_composite[i]$也是类似的判断。

虽然不知道如何验证,但是实事便是,所有的数都可以被常数次遍历。一般情况我们不需要最小素因子,所以可以优化成布尔型经行遍历。

花边

这个算法最早的渊源是和欧拉有关的,但好像不是欧拉自己提出的。

Euler’s proof of the zeta product formula contains a version of the sieve of Eratosthenes in which each composite number is eliminated exactly once.[8] The same sieve was rediscovered and observed to take linear time by Gries & Misra (1978).[19] It, too, starts with a list of numbers from 2 to n in order.

后人观测到了这个算法就发了一篇CACM期刊论文,而巨人欧拉只是以这个方法用来辅助一下更重要的证明(黎曼),没当回事儿···ORZ

但线性筛的概念,可以拓展到类积性函数的求法。去理解这个算法有更长远的意义。

参考

Math note — linear sieve By Nisiyama_Suzune 这篇博客有讲到线性筛

Sieve of Eratosthenes

猜你喜欢

转载自blog.csdn.net/Tighway/article/details/97790452
今日推荐