ebisukeプログラミング初心者脱出黙示録

30歳を過ぎてから始めたプログラミングと競プロの記録。Pythonで取り組んでいます。Arduinoで電子工作も

エラトステネスの篩で素数列挙【Python】

ある値以下の素数を列挙する方法のひとつにエラトステネスの篩(ふるい)があります。

この記事ではPythonでエラトステネスの篩をプログラミングしていきます。


エラトステネスの篩とは

次のように整数n以下の素数を高速にすべて列挙する方法です。

まず2からnまでの数を考え、この数字のなかで最も小さい数字をpとおき、n以下かつp以外のpの倍数をすべて消していきます。

消し終えたら、その中で最も小さい数字をpとおき倍数を消していく・・・という作業を繰り返します。

pが√nを超えたら終了です。

このときに残った数字が素数になります。


素数列挙プログラム(n = 100の場合)

# x以下の素数列挙
def sieve_of_eratosthenes(x):
    nums = [i for i in range(x+1)]

    root = int(pow(x,0.5))
    for i in range(2,root + 1):
        if nums[i] != 0:
            for j in range(i, x+1):
                if i*j >= x+1:
                    break
                nums[i*j] = 0

    primes = sorted(list(set(nums)))[2:]

    return primes


p = sieve_of_eratosthenes(100)

print(p)
# --> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
print(len(p))
# --> 25

解説

nums[i] = iとなる配列を作成して素数の列挙をすすめていきます。

エラトステネスの篩は√nより大きい数字になれば終了してよいのでrootに√nを計算し、このrootを用いてループの範囲は2からroot + 1として進めます。

nums[i]が0なら次の数字にスキップし、0でなければさらにループさせ倍数を削除します。

2重ループではp[i]の倍数であるi × jを0に置き換えました。(nums[i*j] = 0)

この探索を終えるとnumsに残る値は素数か0です。

0が複数あるnumsをset(nums)としてset型を利用して0の重複を削除しました。

このset(nums)を再度list()でリスト化し、ソートします。

これで[0, 1, 2, 3, 5, 7… ,97]という配列になるので、不要になる初めの2つの要素をスライスして取り除きます。

その結果、primes = sorted(list(set(nums)))[2:] = [2, 3, 5, 7… ,97]という求める素数を列挙した配列を作成できました。

primesをlen()で個数を調べることで素数の数を数えることもできます。


上記のプログラムで高速に素数を列挙することができました!




エラトステネスの篩についてこちらの記事が参考になりました。
ありがとうございました。
club.informatix.co.jp