9

C#で特定の小数点以下の桁数までPIを計算するにはどうすればよいですか?

メソッドに数値を渡して、その小数点以下の桁数まで計算されたPIを取り戻すことができるようにしたいと思います。

public decimal CalculatePi(int places)
{
    // magic
    return pi;
}

Console.WriteLine(CalculatePi(5)); // Would print 3.14159

Console.WriteLine(CalculatePi(10)); // Would print 3.1415926535

等...

プログラムの速度は気にしません。できるだけシンプルでわかりやすいものにしたいと思っています。助けてくれてありがとう。

4

6 に答える 6

17

まず、円周率の任意の桁数が必要で、さまざまな浮動小数点数の精度に制限されたくない場合、円周率関数を任意の数値型ではなく文字列として定義します。

この手法を検索しているときに見つけた最も優れたアルゴリズムの 1 つは、Stanley Rabinowitz and Stan Wagon-Spigot Algorithmです。浮動小数点演算を必要とせず、ほとんどが反復的な方法です。中間計算で整数配列を格納するためのメモリが必要です。

コードの合理化やクリーンアップに時間をかけることなく、ここでアルゴリズムを実装します (結果に小数点が追加されないことに注意してください)。

このコードを個人的な目的以外で使用する場合は、必ずアルゴリズムとこのサイトを引用してください。

C# コード

public static string CalculatePi(int digits)
{   
    digits++;

    uint[] x = new uint[digits*10/3+2];
    uint[] r = new uint[digits*10/3+2];
    
    uint[] pi = new uint[digits];

    for (int j = 0; j < x.Length; j++)
        x[j] = 20;
        
    for (int i = 0; i < digits; i++)
    {
        uint carry = 0;
        for (int j = 0; j < x.Length; j++)
        {
            uint num = (uint)(x.Length - j - 1);
            uint dem = num * 2 + 1;

            x[j] += carry;

            uint q = x[j] / dem;
            r[j] = x[j] % dem;

            carry = q * num;
        }
        
        
        pi[i] = (x[x.Length-1] / 10);
            
                    
        r[x.Length - 1] = x[x.Length - 1] % 10; ;
        
        for (int j = 0; j < x.Length; j++)
            x[j] = r[j] * 10;
    }
    
    var result = "";
    
    uint c = 0;
    
    for(int i = pi.Length - 1; i >=0; i--)
    {
        pi[i] += c;
        c = pi[i] / 10;
        
        result = (pi[i] % 10).ToString() + result;
    }

    return result;
}

アップデート

35 桁の後に発生する「キャリー エラー」の修正にようやく取り掛かりました。実際、リンクされたドキュメントの 6 ページには、ここで何が起こっているかが具体的に説明されています。1000桁までの最終バージョンをテストしました。

于 2012-07-26T22:32:32.440 に答える
7
Math.Round(Math.PI, places)

さらに精度が必要な場合は、特定の最大値をサポートする double データ型を使用すると問題が発生します。精度 (Math.PI によって提供されます)。

于 2012-07-26T20:21:39.590 に答える
7

nicholas と同じアルゴリズムですが、遅延評価に yield を使用します

    static public IEnumerable<uint> Pi()
    {
        uint[] x = new uint[short.MaxValue];
        uint[] r = new uint[short.MaxValue];

        for (int j = 0; j < short.MaxValue; j++)
            x[j] = 20;

        for (int i = 0; i < short.MaxValue; i++)
        {
            uint carry = 0;
            for (int j = 0; j < x.Length; j++)
            {
                uint num = (uint)(x.Length - j - 1);
                uint dem = num * 2 + 1;

                x[j] += carry;

                uint q = x[j] / dem;
                r[j] = x[j] % dem;

                carry = q * num;
            }

            yield return (x[x.Length - 1] / 10);

            r[x.Length - 1] = x[x.Length - 1] % 10; ;
            for (int j = 0; j < x.Length; j++)
            {
                x[j] = r[j] * 10;
            }                    
        }
    }

桁数の上限として short.MaxValue を使用しましたが、これはマシンの仮想メモリが不足しているためです。より優れたマシンは、int.MaxValue まで対応できるはずです。

関数は次のように呼び出すことができます。

 class Program
{
    static void Main(string[] args)
    {
        foreach (uint digit in Calculator.Pi().Take(100))
        {
            Console.WriteLine(digit);
        }

        Console.Read();
    }
}
于 2015-01-04T15:18:58.300 に答える
7

多くの検索の後、私はこの小さなスニペットを見つけました:

public static class BigMath
{
    // digits = number of digits to calculate;
    // iterations = accuracy (higher the number the more accurate it will be and the longer it will take.)
    public static BigInteger GetPi(int digits, int iterations)
    {
        return 16 * ArcTan1OverX(5, digits).ElementAt(iterations)
            - 4 * ArcTan1OverX(239, digits).ElementAt(iterations);
    }

    //arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
    public static IEnumerable<BigInteger> ArcTan1OverX(int x, int digits)
    {
        var mag = BigInteger.Pow(10, digits);
        var sum = BigInteger.Zero;
        bool sign = true;
        for (int i = 1; true; i += 2)
        {
            var cur = mag / (BigInteger.Pow(x, i) * i);
            if (sign)
            {
                sum += cur;
            }
            else
            {
                sum -= cur;
            }
            yield return sum;
            sign = !sign;
        }
    }
}

これまでのところ、魅力のように機能しています。BigInteger 型を解決するには、GAC から System.Numerics ライブラリを追加するだけです。

于 2012-07-26T23:13:57.533 に答える
4

ネイティブの数学ライブラリが提供する桁数に満足している場合は、簡単です。希望の桁数に丸めるだけです。さらに多くの桁数 (数十、数百、数千) が必要な場合は、一度に 1 つずつ数字を吐き出すスピゴット アルゴリズムが必要です。Jeremy Gibbons は、私のブログで2 回実装したアルゴリズムを提供しています。ここでは、Scheme、C、Python、Haskell、Perl、および Forth のコードを見つけることができます (ただし、C# ではありません。申し訳ありません)。

于 2012-07-26T20:41:00.093 に答える
2

最も簡単な方法は、多数の桁数 pi を文字列定数に格納することです。次に、桁数の精度が必要な場合はいつでもn、0 から までの部分文字列を取得しn+2ます。

于 2012-07-26T21:53:56.457 に答える