305


79

πの値を得るための最速の方法は何ですか?

私は個人的な課題として、πの値を得るための最速の方法を探しています。 もっと具体的に言うと、私は M_PI`のような#define`定数を使わない、またはで数値をハードコーディングすることを含まない方法を使っています。

以下のプログラムは私が知っているさまざまな方法をテストします。 インラインアセンブリバージョンは、理論的には最速のオプションですが、明らかに移植性はありません。 他のバージョンと比較するためのベースラインとして含めました。 私のテストでは、組み込みで、 4 * atan(1)`バージョンはGCC 4.2で最も速いです、なぜならそれは `atan(1)`を定数に自動折りたたむからです。 `-fno-builtin`を指定した場合、 atan2(0、-1) `バージョンが最速です。

これが主なテストプログラムです( pitimes.c):

#include
#include
#include

#define ITERS 10000000
#define TESTWITH(x) {                                                       \
    diff = 0.0;                                                             \
    time1 = clock();                                                        \
    for (i = 0; i < ITERS; ++i)                                             \
        diff += (x) - M_PI;                                                 \
    time2 = clock();                                                        \
    printf("%s\t=> %e, time => %f\n", #x, diff, diffclock(time2, time1));   \
}

static inline double
diffclock(clock_t time1, clock_t time0)
{
    return (double) (time1 - time0) / CLOCKS_PER_SEC;
}

int
main()
{
    int i;
    clock_t time1, time2;
    double diff;

    /* Warmup. The atan2 case catches GCC's atan folding (which would
     * optimise the ``4 * atan(1) - M_PI'' to a no-op), if -fno-builtin
     * is not used. */
    TESTWITH(4 * atan(1))
    TESTWITH(4 * atan2(1, 1))

#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
    extern double fldpi();
    TESTWITH(fldpi())
#endif

    /* Actual tests start here. */
    TESTWITH(atan2(0, -1))
    TESTWITH(acos(-1))
    TESTWITH(2 * asin(1))
    TESTWITH(4 * atan2(1, 1))
    TESTWITH(4 * atan(1))

    return 0;
}

そして、x86とx64システムでのみ動作するインラインアセンブリ( fldpi.c):

double
fldpi()
{
    double pi;
    asm("fldpi" : "=t" (pi));
    return pi;
}

そして私がテストしているすべての設定をビルドするビルドスクリプト( build.sh):

#!/bin/sh
gcc -O3 -Wall -c           -m32 -o fldpi-32.o fldpi.c
gcc -O3 -Wall -c           -m64 -o fldpi-64.o fldpi.c

gcc -O3 -Wall -ffast-math  -m32 -o pitimes1-32 pitimes.c fldpi-32.o
gcc -O3 -Wall              -m32 -o pitimes2-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -fno-builtin -m32 -o pitimes3-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -ffast-math  -m64 -o pitimes1-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall              -m64 -o pitimes2-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -fno-builtin -m64 -o pitimes3-64 pitimes.c fldpi-64.o -lm

さまざまなコンパイラフラグ間のテストとは別に(最適化が異なるため、32ビットと64ビットを比較しました)、テストの順序を入れ替えることも試しました。 しかし、それでも、 `atan2(0、-1)`バージョンは毎回トップに出ます。

23 Answer


197


前述のように Monte Carloメソッドはいくつかの素晴らしい概念を適用しますが、明らかに最速ではなく、ロングショットではなく、合理的な方法ではありません。 また、それはすべてあなたが探している精度の種類に依存します。 私が知っている最速のπは、数字がハードコードされているものです。 Piとhttp://functions.wolfram.com/PDF/Pi.pdf[Pi[PDF]]を見ると、たくさんの公式があります。

これは素早く収束する方法です - 繰り返しあたり約14桁です。 現在最速のアプリケーションである PiFastはFFTでこの式を使用します。 コードは簡単なので、式を書きます。 この式は、ほぼ Ramanujanによって発見され、Chudnovskyによって発見されました]。 それは実際に彼が数十億桁の数を計算する方法です - それでそれは無視する方法ではありません。 式は急速にオーバーフローし、階乗を分割しているので、そのような計算を遅らせて項を削除するのが有利です。

画像:https://i.stack.imgur.com/aQMkk.gif [ここに画像の説明を入力してください]

image:https://i.stack.imgur.com/2y2l9.gif [ここに画像の説明を入力してください]

どこで、

画像:https://i.stack.imgur.com/QqVnB.gif [ここに画像の説明を入力してください]

下記は Brent–Salaminアルゴリズムです。 ウィキペディアは、* a b が "十分に近い"場合、(a b)²/ 4t *がπの近似値になると述べています。 「十分に近い」とはどういう意味かわかりませんが、私のテストでは、1回の反復で2桁、2回で7回、3回で15回でした。もちろんこれは倍精度です。本当の計算はもっと正確かもしれません。

pi_2 iters =とすると、rec loop_aとすると、i = 0の場合はa、b、t、pとし、そうでない場合はa_n =(aとする。 b)/。 2.0およびb_n = sqrt(a * .b)およびp_n = 2.0 *である。 pをt_n = t  - とする。 (p *。 (a  - 。 a_n)* (a  - 。 a_b)において、loop_a_n b_n t_n p_n(i − 1)において、p = loop_(1.0)(1.0 /。 (sqrt 2.0))(1.0 / .4.0)(1.0)は、(a)になります。 b)*。 (a。 b)/。 (4.0 *。 t)

最後に、いくつかのパイゴルフ(800桁)はどうですか? 160文字!

int a = 10000、b、c = 2800、d、e、f [2801]、g; main(){(; bc;)f [b] = a / 5;(; d = 0、g =) (b = c; d = f [b] * a、f [b] = d%)c * 2; c− = 14、printf(“%.4d”、ed / a)、e = d%a)。 --g、d / = g  - 、 -  b; d * = b);}


110


それはそれ自身の領域を見ることによってπに近似するので、私はこのプログラムが本当に好きです。

IOCCC 1988: westley.c

#define _ -F <00 ||  -  F-OO--; int F = 00、OO = 00; main(){F_OO(); printf( "%1.3f \ n"、4。*  -  F / OO / OO);} F_OO(){_-_-_-_ _-_-_  -  _  -  _  -  _  -  _  -  _  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _ _  -  _  -  _  -  _  -  _  - _-_-_  -  _  -  _  -  _  -  _  -  _  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  - _-_-_  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _ -_-_  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _ -_-_  -  _  -  _  -  _  -  _  -  _  -  _  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _ _ _ -_-_  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  - _-_  -  _  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _  -  _ _  -  _  -  _  -  _  -  _  -  _  -  _  - _-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_}


76


これは私が高校で学んだパイを計算するためのテクニックの一般的な説明です。

私はこれを共有するだけです。なぜなら誰でもそれをいつまでも覚えておくことができるほど単純であり、それが「モンテカルロ」法の概念を教えてくれるからです。ランダムなプロセスを通して控除可能

四角形を描き、その四角形の内側に四分円(四分の一円の四分の一)を刻みます(四角形の半径は四角形の一辺に等しいので、できるだけ四角形の中を埋めます)。

今正方形に投げ矢を投げ、そしてそれが着陸する場所を記録する - すなわち、正方形の中のどこかにランダムな点を選ぶ。 もちろん、それは正方形の内側に上陸しましたが、それは半円の内側にありますか? この事実を記録してください。

このプロセスを何度も繰り返します - そして、あなたは半円形の内側の点の数と投げられた総数の比があることに気付くでしょう。この比をxと呼びます。

正方形の面積はr×rなので、半円の面積はx×r×r(つまり、x×rの2乗)であると推測できます。 それ故、x×4はあなたにpiを与えるでしょう。

これは簡単な使い方ではありません。 しかし、これはモンテカルロ法の良い例です。 そしてあなたが見回すならば、そうでなければあなたの計算能力の外側にある多くの問題がそのような方法によって解決されることができるかもしれません。


54


完全性のために、最適化された構築のために、コンパイル時にPIの近似値を計算し、そして単一の値にインライン化するCテンプレートバージョン。

#include

template
struct sign
{
    enum {value = (I % 2) == 0 ? 1 : -1};
};

template
struct pi_calc
{
    inline static double value ()
    {
        return (pi_calc::value () + pi_calc::value ()) / 2.0;
    }
};

template
struct pi_calc<0, J>
{
    inline static double value ()
    {
        return (sign::value * 4.0) / (2.0 * J + 1.0) + pi_calc<0, J-1>::value ();
    }
};

template<>
struct pi_calc<0, 0>
{
    inline static double value ()
    {
        return 4.0;
    }
};

template
struct pi
{
    inline static double value ()
    {
        return pi_calc::value ();
    }
};

int main ()
{
    std::cout.precision (12);

    const double pi_value = pi<10>::value ();

    std::cout << "pi ~ " << pi_value << std::endl;

    return 0;
}

I> 10については、最適化されていない実行でも同様に、最適化されたビルドが遅くなる可能性があります。 12回の繰り返しの間、value()への呼び出しが約80k回あると思います(メモがない場合)。


42


JonathanとPeter Borweinによる、\ piの計算のための_fast_メソッド専用の本が他にもあります(https://rads.stackoverflow.com/amzn/click/com/)。 047131515X(Amazonで入手可能))。

私はAGMとそれに関連するアルゴリズムをかなり勉強しました。それは非常におもしろいです(時には些細ではありませんが)。

最近のアルゴリズムを実装して\ piを計算するには、多倍長算術ライブラリが必要になります( GMPはかなり良い選択ですが、前回使用してからしばらく時間がかかります)。

最良のアルゴリズムの時間計算量はO(M(n)log(n))にあります。ここで、M(n)は2つのnビット整数の乗算の時間計算量です(M(n)= O(n) log(n)log(log(n)))は、\ piの数字を計算するときに通常必要となるFFTベースのアルゴリズムを使用します。このようなアルゴリズムはGMPで実装されています。

アルゴリズムの背後にある数学は些細なことではないかもしれませんが、アルゴリズム自体は通常数行の疑似コードであり、それらの実装は通常非常に簡単です(あなた自身の多重精度算術を書かないことを選んだ場合:-))。


40


以下の答えは、*これを可能な限り最速の方法で - 最小の計算量で*実行する方法です。 答えが気に入らなくても、それがPIの価値を得る最も早い方法であることを認めなければなりません。

Piの値を得るための* FASTEST *の方法は、次のとおりです。

1)あなたの好きなプログラミング言語を選びました2)そのMathライブラリをロードします3)そしてPiが既にそこで定義されていることを見つけます - 使用の準備ができました!

あなたが手元に数学ライブラリを持っていない場合には..

  • SECOND FASTEST *の方法(より普遍的な解決策)は次のとおりです。

インターネット上でPiを検索します。 ここに:

http://www.eveandersson.com/pi/digits/1000000(100万桁.. あなたの浮動小数点精度は何ですか? )

またはここ:

またはここ:

使用したい精度演算に必要な数字を見つけるのは本当に速いです。定数を定義することで、貴重なCPU時間を無駄にしないようにすることができます。

これは部分的にユーモラスな答えであるだけでなく、実際には、もし誰かが先に進み、実際のアプリケーションでPiの値を計算するならば。 これはかなりのCPU時間の浪費になりますね。 少なくともこれを再計算しようとするための実際のアプリケーションはありません。

モデレーター各位:OPが尋ねたことに注意してください。「PIの値を取得するための最速の方法」


26


http://en.wikipedia.org/wiki/Bailey-Borwein-Plouffe_formula[BBP公式]を使えば、前のn-1桁に煩わされることなく第2桁(または第16桁)でn桁目を計算することができます。最初 :)


22


piを定数として定義する代わりに、常に `acos(-1)`を使います。


21


完全性のためにここにあるべきであるこれに遭遇しました:

http://www.dangermouse.net/esoteric/piet/piet_pi.png[PIで計算する]

精度が向上してプログラムが大きくなるという、かなり優れた特性があります。

http://www.dangermouse.net/esoteric/piet.html[Here]の言語自体に対する洞察


21


http://www.theregister.co.uk/2010/01/06/very_long_pi/[this article]が真実なら、 http://www.theregister.co.uk/2010/01/06/very_long_pi/[algorithm the Bellard]作成されているが利用可能な最速の一つである可能性があります。 彼は、デスクトップPCを使って、2.7兆桁のpiを作成しました。

…​and he has published his ここに仕事

良い仕事Bellard、あなたはパイオニアです!