17年蝉誕生まで その2

アバター
いわん
記事: 32
登録日時: 9年前

17年蝉誕生まで その2

投稿記事 by いわん » 5年前

蝉はひとまずおいといて(間が空いたら興味が薄れた?w)
以前、C#の勉強でつくったn番目の素数を求める自作プログラムを紹介します。
エラトステネスのふるいという手法を使っています。
全ての自然数のテーブルを準備して、素数の倍数を消していく単純ですが非常に高速かつ
もれなく素数を見つけることができる方法です。
問題点はメモリ使用量がとんでもなく肥大化することです。

ふるいの範囲を新しい素数を追加す毎に上方にシフトして、使用メモリを抑える工夫しました。
工夫した部分がよくわからないコードになってしまいましたけど(^^;
なんとか1億番目の素数まではみつけることができました。

CODE:

//n番目素数を求めるプログラム №4
//エラトステネスのふるい改良版
//Compile:
//C:\windows\microsoft.net\framework\v4.0.30319\csc.exe Prime4.cs
//使い方:
// Prime4 <n>(複数指定可)
//

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace Prime4
{
    class Program
    {
        static void Main(string[] args)
        {
            TextWriterTraceListener tl =
				new TextWriterTraceListener(System.IO.File.CreateText("debug.txt"));
            Debug.Listeners.Clear();
            Debug.Listeners.Add(tl);

            foreach (string arg in args)
            {
                int num;
                if (!int.TryParse(arg, out num))
                {
                    continue;
                }
                DateTime dtBegin;
                int n;
                dtBegin = DateTime.Now;
                n = 0;
                foreach (int p in Prime())
                {
                    ++n;
                    //Console.WriteLine(string.Format("{0}:{1}", n, p));
                    if (n == num)
                    {
                        Console.WriteLine(string.Format("{0}番目の素数:{1}", n, p));
                        break;
                    }
                }
                Console.WriteLine(
					string.Format("計算時間:{0,8:#0.###}秒"
						, (DateTime.Now - dtBegin).Ticks / 10000000.0));
            }

            Console.WriteLine(
				string.Format("使用メモリサイズ:{0}MB"
					, Environment.WorkingSet / 1000 / 1000));
            Debug.Flush();
        }

        static List<int> aPrime = new List<int>();
        public static IEnumerable<int> Prime()
        {
            // 最初の素数
            if (aPrime.Count == 0)
            {
                aPrime.Add(2);
                aPrime.Add(3);
            }
            // 作成済み素数出力/新しい素数を見つけるための情報を取得
            int next_x = aPrime[aPrime.Count - 1] + 2;  // 新しい素数候補
            int sieve_prime_order = 0;  // ふるいを作るための最大素数の順位
            int out_order;  // 出力済み素数順位
            for (out_order = 0; out_order < aPrime.Count; ++out_order)
            {
                yield return aPrime[out_order];
                if (aPrime[out_order] * aPrime[out_order] < next_x)
                {
                    sieve_prime_order = out_order;
                }
            }
            int sieve_from = aPrime[sieve_prime_order] * aPrime[sieve_prime_order]; // ふるいの最小値
            ++sieve_prime_order;
            int sieve_to = aPrime[sieve_prime_order] * aPrime[sieve_prime_order];   // ふるいの最大値
            int sieve_prime = aPrime[sieve_prime_order];    // ふるいを作るための最大素数
            // 新しい素数を探す
            while (true)
            {
                int sieve_size = sieve_to - sieve_from; // ふるいサイズ
                bool[] ab = new bool[sieve_size];   // ふるい(false:素因数なし true:素因数あり)
                // sieve_prime 以下の全ての素数について、ふるい中のその素数の倍数に true を設定する
                foreach (int p in aPrime)
                {
                    for (int x = (p - (sieve_from % p)); x < sieve_size; x += p)
                    {
                        ab[x] = true;
                    }
                    if (p == sieve_prime)
                    {
                        break;
                    }
                }
                // ふるいの中の false となっている全ての値を素数として出力する
                for (int i = next_x - sieve_from; i < sieve_size; ++i)
                {
                    if (!ab[i])
                    {
                        aPrime.Add(sieve_from + i);
                        yield return sieve_from + i;
                    }
                }
                // ふるいを作成するための素数を1つ追加して情報を更新する
                sieve_from = sieve_to;
                ++sieve_prime_order;
                sieve_to = aPrime[sieve_prime_order] * aPrime[sieve_prime_order];
                sieve_prime = aPrime[sieve_prime_order];
                next_x = sieve_from + 2;
            }
        }
    }
}

tanu_kichi
記事: 15
登録日時: 5年前

Re: 17年蝉誕生まで その2

投稿記事 by tanu_kichi » 5年前

素数 Wikipedia の中に
「n 番目の素数を求める素数生成式は存在しないと主張されることがあるが、これは誤りである」
とあり、リンクをたどっていくと、Prime Formulas と言うページに恐ろしく複雑な式が書いてありました。

その式をプログラム化した物がないか、ぐぐってみましたが、見つかりませんでした。
インプリメント不可能なのか、それとも、その式をプログラム化するより、いわんさんのようなプログラムを
実行した方がリソース(コーディング・コスト、メモリ消費、実行時間など)が少なくて済むのかも
しれませんね。

アバター
いわん
記事: 32
登録日時: 9年前

Re: 17年蝉誕生まで その2

投稿記事 by いわん » 5年前

n 番目の素数を求める素数生成式・・・
私もいろいろ調べてみましたが、なになやら難解な数学の数式やら英語のページやらで断念しました(;^_^A
エラトステネスのふるいあたりが私の限界です( ̄▽ ̄)
工夫をするとしたら、ふるいをビット単位にしたらさらに生成限界が伸びるかな?速度はどうなるか・・・