1. 程式人生 > 其它 >厄拉多塞篩法

厄拉多塞篩法

厄拉多塞篩法

演算法介紹

厄拉多塞篩演算法(Eratosthenes Sieve)是一種求素數的方法,由古希臘數學家厄拉多塞提出。它的原理是,給定一個數 n,從 2 開始依次將 \(\sqrt{n}\) 以內的素數的倍數標記為合數,標記完成後,剩餘未被標記的數為素數(從 2 開始)。如此可省去檢查每個數的步驟,使篩選素數的過程更加簡單。厄拉多塞篩演算法具體步驟如下:

  1. 讀取輸入的數 n,將 2 到 n 的所有整數記錄在表中
  2. 從 2 開始,劃去表中所有 2 的倍數
  3. 由小到大尋找表中下一個未被劃去的整數,再劃去表中所有該整數的倍數
  4. 重複第(3)步,直到找到的整數大於 \(\sqrt{n}\) 為止
  5. 表中所有未被劃去的整數均為素數

演算法流程圖

具體程式碼(python)

def sieve_prime(n: int) -> list:
    """厄拉託塞篩法"""
    if n < 2:  # 不存在小於 2 的素數
        return []
    is_prime = [True for _ in range(n + 1)]
    # 遍歷 i=2 到 根號 n
    for i in range(2, int(pow(n, 0.5)) + 1):
        if is_prime[i]:  # 篩去 i 的倍數
            for j in range(i * i, n + 1, i):
                is_prime[j] = False
    # 返回 2 到 n 中未被篩去的數
    return [i for i in range(2, n + 1) if is_prime[i]]

在篩去 i 的倍數的時候,第一個數是 \(i \times i\) 而不是 i,這是因為對於所有 \(k \times i , \; k < i\),都在前面被篩過,故可以跳過這些數

測試樣例和結果

  • 102 以內的素數共 25 個,最後兩個為 89, 97
  • 103 以內的素數共 168 個,最後兩個為 991, 997
  • 104 以內的素數共 1229 個,最後兩個為 9967, 9973
  • 105 以內的素數共 9592 個,最後兩個為 99989, 99991
  • 106 以內的素數共 78498 個,最後兩個為 999979, 999983
  • 107 以內的素數共 664579 個,最後兩個為 9999973, 9999991

一些改進

這個演算法很大的一個問題是對空間很不友好,需要儲存很大的一個素數表
除了 2 之外,所有的素數都是奇數,那麼在篩的時候只需要考慮奇數即可
在構建表格的時候,先前將下標 i 對應整數 i,現在可以將下標 i 對應整數 2i+3,節約一半的儲存空間

def sieve_prime(n: int) -> list:
    """厄拉託塞篩法"""
    if n < 2:  # 不存在小於 2 的素數
        return []
    elif n == 2:
        return [2]
    end = (n - 3) // 2
    is_prime = [True for _ in range(end + 1)]
    # 遍歷 i=3 到 根號 n
    for i in range(3, int(pow(n, 0.5)) + 1, 2):
        k = (i - 3) // 2  # 將 i 映射回下標 k
        if is_prime[k]:  # 篩去 i 的倍數
            for j in range(i * i, n + 1, 2 * i):
                is_prime[(j - 3) // 2] = False
    # 返回 2 到 n 中未被篩去的數
    return [2] + [2 * i + 3 for i in range(end + 1) if is_prime[i]]

最後

除了厄拉多塞篩法之外,還有尤拉篩等篩法

使用 python 時間和空間效率都較低,對於標記素數,可以採用 c++ 的 bitset,bitset 是以位元為單位標記的,會極大降低儲存消耗