エラトステネスの篩とMeissel-Lehmer Algorithm
整数問題の実力をつけるため、Meissel-Lehmer Algorithmを紐解いていく
エラトステネスの篩
競プロのために、いろいろなアルゴリズムを自力で実装してストックすることを始めた。 今のところ、以下をライブラリとして Git に push している。
- Next Permutation
- Union Find Tree
- Binary Search:二分探索
- Cumulative Sum:累積和
- Eratosthenes Seive:エラトステネスの篩
今日はエラトステネスの篩を実装していたが、思ったように速度が出ない。Python の遅さも相まって、コンテストで実用に耐えうるのはせいぜいといったところである。(手元の環境でも AtCoder のコードテストでも 700ms 程度かかる)
素数の全列挙が必要なケースでは、エラトステネスの篩で戦わざるを得ないが、単純に素数の個数
を求めるだけであれば、より優れたアルゴリズムがあるようなので、調べていた。これがかなり難しく、なかなか理解が追いつかないので、思考過程も含めてここにアウトプットしておく。
Meissel-Lehmer Algorithm
なぜ高速化を志したかというと、Library-Checker での入力がで、話にならんレベルで高速化が必要だからである。
「こんなん C++専用すぎて Python じゃ通せないだろ」と思って AC 例をあさっていたら、なんとちゃんと AC している人がいる。 強い。これは強い。アルゴリズムの可能性をひしひしと感じた。
幸いにも使用しているアルゴリズムを引用元を記載のうえコードを提出している神のような Submission があったので、勢いいさんで調べてみたもののゴリゴリの整数論でノックアウト寸前である。
それが今回の Meissel-Lehmer Algorithm ということなのだが、ちょっと書ききれるかわからない。というか理解できるかもわからないのが現状である。
現時点でわかっていること
まず、前提となるエラトステネスの篩は、対象となる整数列を小さい方から順番に見ていって、2 の倍数、3 の倍数、5 の倍数……という調子に数を消していくアルゴリズムである。 順番に消していけば、の倍数を消すステップに遷移した際に、必ずは素数である。なぜなら、ある素数を考えた際に、以下でかつと互いに素な数はまでのすべての整数であるからだ。それまでにふるい落とされていないということは、はまでのすべての数と互いに素である。
ここで、ある自然数に対して以下かつと互いに素な自然数の個数をとする。これをオイラーのファイ関数あるいはトーシェント関数などと一般的に呼ぶ。
を素数とすると、という性質や、互いに素な数に対して、という性質を持つ。
前者は、の素因数がのみであることを考えれば、の素因数でないものはの倍数に限定される。それは当然、をで割れば求まるため、導出できる。
後者は、乗法性と呼ばれるがこれが少し難しい。まず、の表を作って考える。
とと互いに素な数だとする。 関数の定義から、1 行あたりとなりうる数は個ある。このとき、で構成される列はの完全剰余系であり、そこに含まれると互いに素である数の個数はとなる。
したがって、各に対して、個ずつと互いに素となる数があるので乗法性が成り立っている。
完全剰余系における互いに素である数の個数については要補足。今後の予定。
Prime counting function
以下に含まれる素数の個数を返す関数を素数計数関数、または関数という。 のように表す。以下の素数は、のつである。
Partial seive function
ここでは、既往のエラトステネスの篩を用いれば求めることが可能であるが、をもっと計算負荷の低い何か別の関数への操作で得ることができれば、計算量を落とすことが可能である。
そこで、部分ふるい関数(partial seive function)という概念を導入する。 これは、で定義される集合である。
式だけ書くと意味不明なので、言葉で解釈すると閉区間にある自然数を、番目までの素数でふるった残りの数であ る。
ここで、番目の素数とは、というようにから小さい順に素数にインデックスを割り付けたときの番号を指す。
th partial seive function
さらに、これを計算するために次の部分ふるい関数を定義する。
これは、で表し、のうち、素因数をちょうど個だけ含む数の集合を表す。
次部分ふるい関数の具体例
例として、を考えてみる。
以下の素数はであり、であるので、番目の素数でふるった残りとなることが分かる。
したがって、以下となる。
さらに、このうち素因数をつも含まないものを部分ふるい関数で表すととなる。
また、素因数をただつ含むもの、すなわち素数それ自身は、である。
部分ふるい関数と次部分ふるい関数の関係性
ここで次のふるい関数で得られる集合は、次数が異なればそれぞれの集合は排他的であるので、以下が言える。
また、以下の式も成り立つ。これは、定義を考えれば自明であろう。
さらに、上の式をについて解けば以下も成り立つことがわかる。
したがって、およびを連立して以下が言える。
力尽きた…
ちょっと重くなってきたので、いったん記事を切ることにする。 そう簡単には「かんぜんにりかいした」 段階にすら至らない重さだ。 終わりが見えない…