欧拉筛

Posted dalt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉筛相关的知识,希望对你有一定的参考价值。

  欧拉筛是线性时间复杂度筛选素数的算法。

  先看一般筛法寻找素数:

findPrime(n)
    isPrime = array
    primes = empty-list
    isPrime[1] = false;
    for(i = 2; i < n; i++)
        isPrime[i] = true
    for(i = 2; i < n; i++)
        if(isPrime[i])
            primes.add(i)
        for p in primes
       if(p * i >= n) break isPrime[i
* p] = false
  return
primes

  先说明上面的代码可以正确找到所有[1,n)之间的素数。如果一个数x是素数,那么isPrime[x]恒为真。如果x为合数,则可以分解为p与x/p,其中p是x的最小素因子。而p,x/p<x,我们不妨设p<=x/p,则当i=x/p时,此时p已经作为素数加入到了primes中,而isPrime[p * i] = false会将x设为合数,因此x会作为合数分离出去。故最后所有的素数都记录在了primes中,且记录在primes中的也只有素数。

  上面的过程是正确的,时间复杂度为∑ln(i)=O(ln(n)*n)。ln(n)是素数定理中对素数分布的估计。

  我们发现上面这个过程中涉及到多次对同一个合数设置其isPrime属性,比如12,会由于4*3以及6*2被两次设置,而由于多次设置的现象存在,我们很难将时间复杂度优化到线性。而欧拉筛则在上面这个素数筛的基础上,要求只有当i取值为x的最大因子时,才允许设置x为合数。而实现出乎意料的简单,只用加上一行代码。

findPrime(limit)
   isPrime = array
   primes = empty-list
   isPrime[1] = false;
   for(i = 2; i < limit; i++)
       isPrime[i] = true
   for(i = 2; i < limit; i++)
       if(isPrime[i])
           primes.add(i)
       for p in primes
       if(i * p >= n) break  isPrime[i * p] = false
       if(i % p == 0) break
  return primes

  先说明这个算法是正确的。当i>p且能整除p时,对于任意素数k>p,ik的最大因子显然不是i(至少为pk),因此可以直接跳过后面的流程。当然还有一种特殊情况就是i=p,此时跳出与不跳出都不影响结果,因为循环已经濒临结束了。而对于primes中最终结果的正确性与上面算法的证明一致。

  对于时间复杂度,外部循环最多执行n次,每次都是常数时间复杂度,而内部循环每次执行都会设置一个合数的isPrime属性,而每个合数只会被设置一次,因此内部循环执行的次数与合数总数等阶,即为O(n)。因此总的时间复杂度为O(n)。

 

以上是关于欧拉筛的主要内容,如果未能解决你的问题,请参考以下文章

欧拉筛&&线性筛

关于欧拉筛筛素数

欧拉筛法模板代码

欧拉筛转载自用

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)