コンテンツにスキップ

JavaScript/Generator/素数ジェネレター

出典: フリー教科書『ウィキブックス(Wikibooks)』

JavaScriptにおける「ジェネレータ関数」の力を活用して、素数を無限に生成する「エラトステネスの篩(Sieve of Eratosthenes)」を構築する方法をご紹介します。ジェネレータは、遅延評価とステートフルなイテレーションを実現する非常に強力な構文です。この記事ではその応用として、無限の整数ストリームから素数だけを抽出する実用的かつエレガントなパターンを示します。

// 2, 3, 4, ...という無限の数列を生成するジェネレータ関数
function* generate() {
  for (let i = 2;; i++) {
    yield i;
  }
}

// フィルタジェネレータ関数
// 'in'ジェネレータから値を受け取り、'prime'で割り切れない値を返す
function* filter(inputGenerator, prime) {
  for (const i of inputGenerator) {
    if (i % prime !== 0) {
      yield i;
    }
  }
}

// 素数篩 (Sieve) のジェネレータ関数
// 素数を生成する
function* sieve() {
  let inputGenerator = generate(); // 最初の入力は無限数列のジェネレータ
  for (;;) {
    const prime = inputGenerator.next().value; // 次の素数を取得
    yield prime; // 素数を出力
    // 取得した素数でフィルタリングする新しいジェネレータを作成
    inputGenerator = filter(inputGenerator, prime);
  }
}

// 素数を100まで出力
function main() {

  for (const prime of sieve()) {
    if (prime > 100) {
      break;
    }
    console.log(prime);
  }
}

main();

ステップ1:無限数列のジェネレータ

[編集]

まず、2 から始まる無限の整数列を生成するジェネレータ generate を用意します。

function* generate() {
  for (let i = 2;; i++) {
    yield i;
  }
}

これは、for ループが無限に続くことで、任意のタイミングで .next() を呼べば次の整数が取得できます。

ステップ2:フィルタ関数による除外

[編集]

次に、ある素数 prime で割り切れない整数だけを通す フィルタジェネレータを定義します:

function* filter(inputGenerator, prime) {
  for (const i of inputGenerator) {
    if (i % prime !== 0) {
      yield i;
    }
  }
}

この関数は、入力となる整数ストリーム inputGenerator を監視し、prime の倍数を除外して次のジェネレータへバトンタッチします。

ステップ3:エラトステネスの篩をジェネレータで表現

[編集]

この filter を段階的に組み合わせて素数だけを取り出す「篩」の役割を果たすジェネレータがこちらです:

function* sieve() {
  let inputGenerator = generate(); // 無限の整数列

  for (;;) {
    const prime = inputGenerator.next().value; // 次の素数を取得
    yield prime; // 見つかった素数を出力
    // 素数の倍数を除いた新しいジェネレータを構築
    inputGenerator = filter(inputGenerator, prime);
  }
}

この sieve 関数では、見つかった素数でフィルタリングを繰り返しながら、常に次の素数を抽出します。この構造そのものが「篩(Sieve)」の概念を忠実に再現していると言えるでしょう。

ステップ4:素数を出力する main 関数

[編集]

最後に、100 以下の素数を出力する実行関数です:

function main() {
  for (const prime of sieve()) {
    if (prime > 100) {
      break;
    }
    console.log(prime);
  }
}
main();

この main 関数は、sieve() から素数を順に受け取り、100 を超えるまで出力します。

出力例

[編集]
2
3
5
7
11
13
17
19
23
29
31
...
97

このコードの特徴と利点

[編集]
  • 無限ストリームを効率的に処理できる
  • 状態を持つ関数の再帰的な構成が可能(関数型プログラミング的アプローチ)
  • ✅ 必要な数だけ素数を取り出す遅延評価により無駄がない
  • ✅ 低メモリ・高効率なフィルタリング

応用例と発展

[編集]

このアプローチは、以下のような用途にも応用できます:

  • 任意の条件によるストリームのフィルタリング
  • イベントストリーム処理やリアクティブプログラミング
  • 無限系列の中から特定パターンを抽出する解析ツール

まとめ

[編集]

JavaScriptのジェネレータは、単なる繰り返し構文を超えて、「流れ」を制御する表現力豊かな手段です。エラトステネスの篩という古典的なアルゴリズムと、現代的なジェネレータを組み合わせることで、簡潔かつ効率的な素数生成が実現できます。