欧拉筛

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

  先看通常筛法寻找素数:优化

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中的也只有素数。spa

  上面的过程是正确的,时间复杂度为$\sum_{i=1}^{n}{\frac{n}{i}}=n\sum_{i=1}^{n}{\frac{1}{i}}=n\ln n$。code

  咱们发现上面这个过程当中涉及到屡次对同一个合数设置其isPrime属性,好比12,会因为4*3以及6*2被两次设置,而因为屡次设置的现象存在,咱们很难将时间复杂度优化到线性。而欧拉筛则在上面这个素数筛的基础上,保证每一个合数仅被设置一次。而实现出乎意料的简单,只用加上一行代码。blog

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中最终结果的正确性与上面算法的证实一致。it

  再说明每一个合数m的isPrime仅被设置一次。考虑当咱们设置isPrime[m]时,有i*p=m,其中p为素数且p<=i。若咱们在以前对isPrime[m]进行了另一次设置,这意味着j*p*k=m,其中j*k=i,j*p<i,很显然k=m/(j*p)>m/i=p,然而因为p|(j*p),所以在访问到素数k时咱们已经跳出了循环,因此在以前设置isPrime[m]是不可能的。class

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

相关文章
相关标签/搜索