Euler筛法用于素数筛选

来源:互联网 发布:直播app数据库设计 编辑:程序博客网 时间:2024/05/20 19:31

Euler筛法

介绍

这是复杂度非常低的一个素数筛法,大部分资料显示时间复杂度仅为O(n),其核心在于对于每一个数仅仅筛了一次.

标准代码

void euler_sieve(int n) {    totPrimes = 0;    for (int i = 2; i <= n; i++) {        if (!flag[i])            primes[totPrimes++] = i;        for (int j = 0; i * primes[j] <= n; j++) {            flag[i * primes[j]] = true;            if (i % primes[j] == 0)                break;        }    }}

解释

首先很容易看出,iprime[i] 表示由 i(不一定是质数)乘上一个质数 prime[i] 产生的合数,于是就可以把这个产生的合数筛掉,但是要尽量少地去筛,否则就和Erathosthenes筛法(复杂度 O(nlognlogn) ,大量重复筛选)一样了.

那么怎么做才能只筛一次?关键是找出每个合数的独一无二的特性,根据这个特性去筛.

重要结论

n=FactormaxP

每个合数n都可以表示成这种形式,其中Factormax表示n的最大因数,并且剩下的这个 P 满足:

  1. 它是一个素数.
  2. 它比 Factormax 的所有因数都要小.

Pn最小素因数.

证明如下:

  1. 首先假设P不是素数,那么就有 P=P1P2Pn ,其中 P1,P2PnP1<P2<<Pn 为素数.

    于是肯定有 FactormaxP1Factor 要大,那么 Factormax 就不是最大的因数了,which contradicts the definition.

  2. 再者,我们设 Factormax=P1P2Pn ,假设 P>P1 ,那么显然 PP2Pn 要比 Factor 更大,which contradicts the definition.

由这个结论引出的Euler筛

假设有一个素数表 prime[n] 和一个是否为合数的标记表 flag[n] (定义 true 表示合数). 我们每次枚举一个数 i,判断它是否标记过,如果是,那么存入 prime[n] ,否则不存. 接下来我们标记i (不论它是素数与否)产生的数 n=iprime[n] 为合数,因为显然质数和另一个数(>2 )相乘为合数.

根据以上结论,我们对于一个数,把它作为另一个数最大因子,那么显然可以根据已有的质数表产生一些合数. 并且我们知道这些合数显然只有一种产生方法(只有一个 Factormax )那么我们每次根据枚举到的 i 只要判定prime[n] 是不是最小素因数就可以保证枚举的量不多不少.

根据 prime[n]i 来判定是否完全枚举

prime[n]i 时,我们可以知道对于 i=P1P2PnPx=prime[n] ,由于我们的 prime[n] 是升序地枚举求出的,于是当满足这个条件之前,是一直有 prime[k],k<n 为最小素因数这个性质的,于是在恰好满足的时候,由 i 产生的合数就枚举完了,至于没有标记的 iprime[n] 以及后面所有没有标记的,由于 i 不是它的最大因数,在接下来当枚举到的更大的 j 是它的最大因数时,就一定会被标记到.