歐拉篩

  歐拉篩是線性時間複雜度篩選素數的算法。算法

  先看通常篩法尋找素數:優化

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)。基礎

相關文章
相關標籤/搜索