Apa cara tercepat untuk mendapatkan nilai π?


322

Saya mencari cara tercepat untuk mendapatkan nilai π, sebagai tantangan pribadi. Lebih khusus lagi, saya menggunakan cara-cara yang tidak melibatkan penggunaan #definekonstanta seperti M_PI, atau hard-coding nomor.

Program di bawah ini menguji berbagai cara yang saya ketahui. Versi perakitan inline, secara teori, adalah pilihan tercepat, meskipun jelas tidak portabel. Saya memasukkannya sebagai data dasar untuk dibandingkan dengan versi lainnya. Dalam pengujian saya, dengan built-in, 4 * atan(1)versi ini tercepat di GCC 4.2, karena otomatis melipat atan(1)menjadi konstanta. Dengan -fno-builtinditentukan, atan2(0, -1)versi ini tercepat.

Inilah program pengujian utama ( pitimes.c):

#include <math.h>
#include <stdio.h>
#include <time.h>

#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;
}

Dan hal-hal perakitan inline ( fldpi.c) yang hanya akan bekerja untuk sistem x86 dan x64:

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

Dan skrip build yang membangun semua konfigurasi yang saya uji ( 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

Terlepas dari pengujian antara berbagai flag compiler (saya telah membandingkan 32-bit dengan 64-bit juga karena pengoptimalannya berbeda), saya juga telah mencoba mengganti urutan pengujian. Tapi tetap saja, atan2(0, -1)versi ini masih keluar setiap saat.


38
Pasti ada cara untuk melakukannya di metaprogramming C ++. Waktu berjalan akan sangat bagus, tetapi waktu kompilasi tidak akan baik.
David Thornley

1
Mengapa Anda menganggap menggunakan atan (1) berbeda dari menggunakan M_PI? Saya mengerti mengapa Anda ingin melakukan ini jika Anda hanya menggunakan operasi aritmatika, tetapi dengan atan saya tidak mengerti intinya.
erikkallen

9
pertanyaannya adalah: mengapa Anda tidak ingin menggunakan konstanta? misalnya apakah ditentukan oleh perpustakaan atau oleh Anda sendiri? Komputasi Pi adalah pemborosan siklus CPU, karena masalah ini telah dipecahkan berulang-ulang ke sejumlah digit signifikan yang jauh lebih besar dari yang dibutuhkan untuk perhitungan harian
Tilo

2
@ HopelessN00b Dalam dialek bahasa Inggris saya berbicara, "optimasi" dieja dengan "s", bukan "z" (yang diucapkan sebagai "zed", BTW, bukan "zee" ;-)). (Ini bukan pertama kalinya saya harus mengembalikan jenis pengeditan ini juga, jika Anda melihat riwayat ulasan.)
Chris Jester-Young

Jawaban:


205

Metode Monte Carlo , seperti yang disebutkan, menerapkan beberapa konsep hebat tetapi jelas, bukan yang tercepat, bukan melalui upaya yang panjang, bukan dengan ukuran yang masuk akal. Semua itu tergantung pada akurasi yang Anda cari. Fastest tercepat yang saya tahu adalah yang memiliki digit kode keras. Melihat Pi dan Pi [PDF] , ada banyak formula.

Berikut adalah metode yang konvergen dengan cepat - sekitar 14 digit per iterasi. PiFast , aplikasi tercepat saat ini, menggunakan rumus ini dengan FFT. Saya hanya akan menulis formula, karena kodenya mudah. Formula ini hampir ditemukan oleh Ramanujan dan ditemukan oleh Chudnovsky . Sebenarnya cara dia menghitung beberapa miliar digit angka - jadi itu bukan metode untuk mengabaikan. Formula akan meluap dengan cepat dan, karena kita membagi faktorial, akan menguntungkan jika menunda perhitungan seperti itu untuk menghapus istilah.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

dimana,

masukkan deskripsi gambar di sini

Di bawah ini adalah algoritma Brent-Salamin . Wikipedia menyebutkan bahwa ketika a dan b "cukup dekat" maka (a + b) ² / 4t akan menjadi perkiraan π. Saya tidak yakin apa artinya "cukup dekat", tetapi dari tes saya, satu iterasi mendapat 2 digit, dua mendapat 7, dan tiga memiliki 15, tentu saja ini dengan ganda, sehingga mungkin memiliki kesalahan berdasarkan pada representasi dan yang benar perhitungan bisa lebih akurat.

let pi_2 iters =
    let rec loop_ a b t p i =
        if i = 0 then a,b,t,p
        else
            let a_n = (a +. b) /. 2.0 
            and b_n = sqrt (a*.b)
            and p_n = 2.0 *. p in
            let t_n = t -. (p *. (a -. a_n) *. (a -. a_n)) in
            loop_ a_n b_n t_n p_n (i - 1)
    in 
    let a,b,t,p = loop_ (1.0) (1.0 /. (sqrt 2.0)) (1.0/.4.0) (1.0) iters in
    (a +. b) *. (a +. b) /. (4.0 *. t)

Terakhir, bagaimana dengan beberapa pi golf (800 digit)? 160 karakter!

int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}

1
Dengan asumsi Anda mencoba mengimplementasikan yang pertama sendiri, bukankah sqr (k3) akan menjadi masalah? Saya cukup yakin ini akan menghasilkan bilangan irasional yang harus Anda perkirakan (IIRC, semua akar yang bukan bilangan bulat adalah irasional). Segala sesuatu yang lain terlihat sangat mudah jika Anda menggunakan aritmatika presisi tak terbatas tetapi akar kuadrat itu adalah pemecah kesepakatan. Yang kedua termasuk sqrt juga.
Bill K

2
dalam pengalaman saya, 'cukup dekat' biasanya berarti ada pendekatan seri taylor yang terlibat.
Stephen

117

Saya sangat suka program ini, karena mendekati π dengan melihat area sendiri.

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()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

1
Jika Anda mengganti _ dengan -F <00 || --F-OO-- akan lebih mudah untuk mengikuti :-)
Pat

1
atau, jika Anda mengganti _ dengan "if (karakter sebelumnya adalah '-') {OO--;} F--;"
FryGuy

6
itu mencetak 0,25 di sini -.-
Johannes Schaub - litb

8
Program ini hebat pada tahun 1998, tetapi rusak karena preprosesor modern lebih liberal dengan memasukkan ruang di sekitar ekspansi makro untuk mencegah hal-hal seperti ini bekerja. Sayangnya, ini adalah peninggalan.
Chris Lutz

38
Lewati --traditional-cppke cpp untuk mendapatkan perilaku yang diinginkan.
Nietzche-jou

78

Berikut adalah gambaran umum tentang teknik menghitung pi yang saya pelajari di sekolah menengah.

Saya hanya membagikan ini karena saya pikir itu cukup sederhana sehingga siapa pun dapat mengingatnya, tanpa batas waktu, ditambah lagi mengajarkan Anda konsep metode "Monte-Carlo" - yang merupakan metode statistik untuk sampai pada jawaban yang tidak langsung tampak seperti dapat dikurangkan melalui proses acak.

Gambarlah sebuah persegi, dan tuliskan kuadran (seperempat setengah lingkaran) di dalam persegi itu (kuadran dengan jari-jari sama dengan sisi persegi, sehingga memenuhi sebanyak mungkin dari persegi itu)

Sekarang lempar anak panah ke alun-alun, dan catat di mana ia mendarat - yaitu, pilih titik acak di mana saja di dalam alun-alun. Tentu saja, itu mendarat di dalam alun-alun, tetapi apakah itu di dalam setengah lingkaran? Catat fakta ini.

Ulangi proses ini berkali-kali - dan Anda akan menemukan rasio jumlah titik di dalam setengah lingkaran versus jumlah total yang dilemparkan, sebut rasio ini x.

Karena luas kotak adalah r kali r, Anda dapat menyimpulkan bahwa luas setengah lingkaran adalah x kali r kali r (yaitu, x kali r kuadrat). Maka x kali 4 akan memberi Anda pi.

Ini bukan metode cepat untuk digunakan. Tetapi ini adalah contoh yang bagus dari metode Monte Carlo. Dan jika Anda melihat-lihat, Anda mungkin menemukan bahwa banyak masalah di luar kemampuan komputasi Anda dapat diselesaikan dengan metode seperti itu.


2
Ini adalah metode yang kami gunakan untuk menghitung Pi dalam proyek java di sekolah. Hanya menggunakan pengacak untuk menghasilkan koordinat x, y dan semakin banyak 'panah' yang kita lemparkan semakin dekat dengan Pi kita datang.
Jeff Keslinke

55

Untuk kepentingan kelengkapan, versi template C ++, yang, untuk build yang dioptimalkan, akan menghitung perkiraan PI pada waktu kompilasi, dan akan sejalan dengan satu nilai.

#include <iostream>

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

template<int I, int J>
struct pi_calc
{
    inline static double value ()
    {
        return (pi_calc<I-1, J>::value () + pi_calc<I-1, J+1>::value ()) / 2.0;
    }
};

template<int J>
struct pi_calc<0, J>
{
    inline static double value ()
    {
        return (sign<J>::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<int I>
struct pi
{
    inline static double value ()
    {
        return pi_calc<I, I>::value ();
    }
};

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

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

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

    return 0;
}

Catatan untuk I> 10, build yang dioptimalkan bisa lambat, juga untuk run yang tidak dioptimalkan. Untuk 12 iterasi, saya percaya ada sekitar 80 ribu panggilan untuk menghargai () (dengan tidak adanya memoisasi).


Saya menjalankan ini dan mendapatkan "pi ~ 3.14159265383"
maxwellb

5
Nah, itu akurat untuk 9dp. Apakah Anda keberatan dengan sesuatu atau hanya melakukan pengamatan?
jon-hanson

apa nama algoritma yang digunakan di sini untuk menghitung PI?
Sebastião Miranda

1
@ sebastião-miranda formula Leibniz , dengan percepatan rata-rata meningkatkan konvergensi. pi_calc<0, J>menghitung setiap istilah berturut-turut dari rumus dan non-spesialis pi_calc<I, J>menghitung rata-rata.
jon-hanson

43

Sebenarnya ada seluruh buku yang didedikasikan (antara lain) untuk metode cepat untuk perhitungan \ pi: 'Pi dan RUPS', oleh Jonathan dan Peter Borwein ( tersedia di Amazon ).

Saya mempelajari AGM dan algoritma terkait cukup sedikit: ini cukup menarik (meskipun kadang-kadang non-sepele).

Perhatikan bahwa untuk mengimplementasikan sebagian besar algoritma modern untuk menghitung \ pi, Anda akan memerlukan pustaka aritmatika multiprecision ( GMP) adalah pilihan yang cukup bagus, meskipun sudah lama sejak saya terakhir menggunakannya).

Kompleksitas waktu dari algoritma terbaik adalah dalam O (M (n) log (n)), di mana M (n) adalah kompleksitas waktu untuk perkalian dua bilangan bulat n-bit (M (n) = O (n) log (n) log (log (n))) menggunakan algoritma berbasis FFT, yang biasanya diperlukan ketika menghitung digit \ pi, dan algoritma seperti itu diimplementasikan dalam GMP).

Perhatikan bahwa meskipun matematika di balik algoritma mungkin tidak sepele, algoritme itu sendiri biasanya beberapa baris kode semu, dan implementasinya biasanya sangat mudah (jika Anda memilih untuk tidak menulis aritmatika multiprecision Anda sendiri :-)).


42

Jawaban berikut tepatnya bagaimana melakukan ini dengan cara tercepat yang mungkin - dengan upaya komputasi yang paling sedikit . Bahkan jika Anda tidak menyukai jawabannya, Anda harus mengakui bahwa itu memang cara tercepat untuk mendapatkan nilai PI.

Cara TERCEPAT untuk mendapatkan nilai Pi adalah:

1) pilih bahasa pemrograman favorit Anda 2) muat perpustakaan Matematika 3) dan temukan bahwa Pi sudah didefinisikan di sana - siap digunakan!

Jika Anda tidak memiliki perpustakaan matematika di tangan ..

Cara TERCEPAT KEDUA (solusi yang lebih universal) adalah:

mencari Pi di Internet, misalnya di sini:

http://www.eveandersson.com/pi/digits/1000000 (1 juta digit .. apa ketepatan titik apung Anda?)

atau di sini:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

atau di sini:

http://en.wikipedia.org/wiki/Pi

Sangat cepat untuk menemukan angka yang Anda butuhkan untuk aritmatika presisi apa pun yang ingin Anda gunakan, dan dengan menetapkan konstanta, Anda dapat memastikan bahwa Anda tidak membuang waktu CPU yang berharga.

Bukan hanya ini jawaban yang agak lucu, tetapi pada kenyataannya, jika ada yang akan melanjutkan dan menghitung nilai Pi dalam aplikasi nyata .. itu akan menjadi pemborosan waktu CPU, bukan? Setidaknya saya tidak melihat aplikasi nyata untuk mencoba menghitung ulang ini.

Moderator yang terhormat: harap dicatat bahwa OP bertanya: "Cara tercepat untuk mendapatkan nilai PI"


Dear Tilo: harap perhatikan bahwa OP berkata: "Saya mencari cara tercepat untuk mendapatkan nilai π, sebagai tantangan pribadi. Lebih khusus, saya menggunakan cara yang tidak melibatkan penggunaan konstanta #define seperti M_PI , atau hard-coding nomor .
Maks

Dear @ Max: harap dicatat bahwa OP mengedit pertanyaan asli mereka setelah saya menjawabnya - itu bukan kesalahan saya;) Solusi saya masih merupakan cara tercepat, dan menyelesaikan masalah dengan presisi floating point yang diinginkan dan tidak ada siklus CPU yang elegan :)
Tilo

Oh maaf, saya tidak menyadarinya. Hanya sebuah pemikiran, bukankah konstanta yang dikodekan memiliki ketelitian yang kurang dari menghitung pi? Saya kira itu tergantung pada bahasa apa itu dan seberapa bersedia penciptanya untuk memasukkan semua digit :-)
Maks

1
Sial, aku lupa menambahkan Dear Tilo
Max

27

The Rumus BBP memungkinkan Anda untuk menghitung angka-n - dalam basis 2 (atau 16) - tanpa harus repot-repot dengan sebelumnya n-1 digit pertama :)


23

Alih-alih mendefinisikan pi sebagai konstanta, saya selalu menggunakan acos(-1).


2
cos (-1), atau acos (-1)? :-P Itu (yang terakhir) adalah salah satu kasus uji dalam kode asli saya. Ini di antara saya lebih disukai (bersama dengan atan2 (0, -1), yang benar-benar sama dengan acos (-1), kecuali bahwa acos biasanya diimplementasikan dalam hal atan2), tetapi beberapa kompiler mengoptimalkan untuk 4 * atan (1) !
Chris Jester-Young

21

Ini adalah metode "klasik", sangat mudah diimplementasikan. Implementasi ini dalam python (bukan bahasa tercepat) melakukannya:

from math import pi
from time import time


precision = 10**6 # higher value -> higher precision
                  # lower  value -> higher speed

t = time()

calc = 0
for k in xrange(0, precision):
    calc += ((-1)**k) / (2*k+1.)
calc *= 4. # this is just a little optimization

t = time()-t

print "Calculated: %.40f" % calc
print "Constant pi: %.40f" % pi
print "Difference: %.40f" % abs(calc-pi)
print "Time elapsed: %s" % repr(t)

Anda dapat menemukan informasi lebih lanjut di sini .

Bagaimanapun, cara tercepat untuk mendapatkan nilai pi dalam python yang tepat sebanyak yang Anda inginkan adalah:

from gmpy import pi
print pi(3000) # the rule is the same as 
               # the precision on the previous code

Berikut ini adalah bagian dari sumber untuk metode gmpy pi, saya tidak berpikir kode ini berguna seperti komentar dalam kasus ini:

static char doc_pi[]="\
pi(n): returns pi with n bits of precision in an mpf object\n\
";

/* This function was originally from netlib, package bmp, by
 * Richard P. Brent. Paulo Cesar Pereira de Andrade converted
 * it to C and used it in his LISP interpreter.
 *
 * Original comments:
 * 
 *   sets mp pi = 3.14159... to the available precision.
 *   uses the gauss-legendre algorithm.
 *   this method requires time o(ln(t)m(t)), so it is slower
 *   than mppi if m(t) = o(t**2), but would be faster for
 *   large t if a faster multiplication algorithm were used
 *   (see comments in mpmul).
 *   for a description of the method, see - multiple-precision
 *   zero-finding and the complexity of elementary function
 *   evaluation (by r. p. brent), in analytic computational
 *   complexity (edited by j. f. traub), academic press, 1976, 151-176.
 *   rounding options not implemented, no guard digits used.
*/
static PyObject *
Pygmpy_pi(PyObject *self, PyObject *args)
{
    PympfObject *pi;
    int precision;
    mpf_t r_i2, r_i3, r_i4;
    mpf_t ix;

    ONE_ARG("pi", "i", &precision);
    if(!(pi = Pympf_new(precision))) {
        return NULL;
    }

    mpf_set_si(pi->f, 1);

    mpf_init(ix);
    mpf_set_ui(ix, 1);

    mpf_init2(r_i2, precision);

    mpf_init2(r_i3, precision);
    mpf_set_d(r_i3, 0.25);

    mpf_init2(r_i4, precision);
    mpf_set_d(r_i4, 0.5);
    mpf_sqrt(r_i4, r_i4);

    for (;;) {
        mpf_set(r_i2, pi->f);
        mpf_add(pi->f, pi->f, r_i4);
        mpf_div_ui(pi->f, pi->f, 2);
        mpf_mul(r_i4, r_i2, r_i4);
        mpf_sub(r_i2, pi->f, r_i2);
        mpf_mul(r_i2, r_i2, r_i2);
        mpf_mul(r_i2, r_i2, ix);
        mpf_sub(r_i3, r_i3, r_i2);
        mpf_sqrt(r_i4, r_i4);
        mpf_mul_ui(ix, ix, 2);
        /* Check for convergence */
        if (!(mpf_cmp_si(r_i2, 0) && 
              mpf_get_prec(r_i2) >= (unsigned)precision)) {
            mpf_mul(pi->f, pi->f, r_i4);
            mpf_div(pi->f, pi->f, r_i3);
            break;
        }
    }

    mpf_clear(ix);
    mpf_clear(r_i2);
    mpf_clear(r_i3);
    mpf_clear(r_i4);

    return (PyObject*)pi;
}

EDIT: Saya punya beberapa masalah dengan cut dan paste dan indentasi, Anda dapat menemukan sumbernya di sini .



18

Jika Anda bersedia menggunakan aproksimasi, 355 / 113bagus untuk 6 digit desimal, dan memiliki keuntungan tambahan karena dapat digunakan dengan ekspresi integer. Itu tidak sepenting akhir-akhir ini, karena "co-processor matematika floating point" tidak lagi memiliki makna, tapi itu cukup penting sekali.


18

Gunakan formula mirip Machin

176 * arctan (1/57) + 28 * arctan (1/239) - 48 * arctan (1/682) + 96 * arctan(1/12943) 

[; \left( 176 \arctan \frac{1}{57} + 28 \arctan \frac{1}{239} - 48 \arctan \frac{1}{682} + 96 \arctan \frac{1}{12943}\right) ;], for you TeX the World people.

Diimplementasikan dalam Skema, misalnya:

(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))


16

Dengan ganda:

4.0 * (4.0 * Math.Atan(0.2) - Math.Atan(1.0 / 239.0))

Ini akan akurat hingga 14 tempat desimal, cukup untuk mengisi dua kali lipat (ketidaktepatan ini mungkin karena sisa desimal dalam garis singgung busur terpotong).

Juga Seth, ini 3.14159265358979323846 3 , bukan 64.


16

Pi tepat 3! [Prof. Frink (Simpsons)]

Lelucon, tapi ini satu di C # (.NET-Framework diperlukan).

using System;
using System.Text;

class Program {
    static void Main(string[] args) {
        int Digits = 100;

        BigNumber x = new BigNumber(Digits);
        BigNumber y = new BigNumber(Digits);
        x.ArcTan(16, 5);
        y.ArcTan(4, 239);
        x.Subtract(y);
        string pi = x.ToString();
        Console.WriteLine(pi);
    }
}

public class BigNumber {
    private UInt32[] number;
    private int size;
    private int maxDigits;

    public BigNumber(int maxDigits) {
        this.maxDigits = maxDigits;
        this.size = (int)Math.Ceiling((float)maxDigits * 0.104) + 2;
        number = new UInt32[size];
    }
    public BigNumber(int maxDigits, UInt32 intPart)
        : this(maxDigits) {
        number[0] = intPart;
        for (int i = 1; i < size; i++) {
            number[i] = 0;
        }
    }
    private void VerifySameSize(BigNumber value) {
        if (Object.ReferenceEquals(this, value))
            throw new Exception("BigNumbers cannot operate on themselves");
        if (value.size != this.size)
            throw new Exception("BigNumbers must have the same size");
    }

    public void Add(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] +
                            value.number[index] + carry;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                carry = 1;
            else
                carry = 0;
            index--;
        }
    }
    public void Subtract(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 borrow = 0;
        while (index >= 0) {
            UInt64 result = 0x100000000U + (UInt64)number[index] -
                            value.number[index] - borrow;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                borrow = 0;
            else
                borrow = 1;
            index--;
        }
    }
    public void Multiply(UInt32 value) {
        int index = size - 1;
        while (index >= 0 && number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] * value + carry;
            number[index] = (UInt32)result;
            carry = (UInt32)(result >> 32);
            index--;
        }
    }
    public void Divide(UInt32 value) {
        int index = 0;
        while (index < size && number[index] == 0)
            index++;

        UInt32 carry = 0;
        while (index < size) {
            UInt64 result = number[index] + ((UInt64)carry << 32);
            number[index] = (UInt32)(result / (UInt64)value);
            carry = (UInt32)(result % (UInt64)value);
            index++;
        }
    }
    public void Assign(BigNumber value) {
        VerifySameSize(value);
        for (int i = 0; i < size; i++) {
            number[i] = value.number[i];
        }
    }

    public override string ToString() {
        BigNumber temp = new BigNumber(maxDigits);
        temp.Assign(this);

        StringBuilder sb = new StringBuilder();
        sb.Append(temp.number[0]);
        sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);

        int digitCount = 0;
        while (digitCount < maxDigits) {
            temp.number[0] = 0;
            temp.Multiply(100000);
            sb.AppendFormat("{0:D5}", temp.number[0]);
            digitCount += 5;
        }

        return sb.ToString();
    }
    public bool IsZero() {
        foreach (UInt32 item in number) {
            if (item != 0)
                return false;
        }
        return true;
    }

    public void ArcTan(UInt32 multiplicand, UInt32 reciprocal) {
        BigNumber X = new BigNumber(maxDigits, multiplicand);
        X.Divide(reciprocal);
        reciprocal *= reciprocal;

        this.Assign(X);

        BigNumber term = new BigNumber(maxDigits);
        UInt32 divisor = 1;
        bool subtractTerm = true;
        while (true) {
            X.Divide(reciprocal);
            term.Assign(X);
            divisor += 2;
            term.Divide(divisor);
            if (term.IsZero())
                break;

            if (subtractTerm)
                this.Subtract(term);
            else
                this.Add(term);
            subtractTerm = !subtractTerm;
        }
    }
}

15

Hitung PI pada waktu kompilasi dengan D.

(Disalin dari DSource.org )

/** Calculate pi at compile time
 *
 * Compile with dmd -c pi.d
 */
module calcpi;

import meta.math;
import meta.conv;

/** real evaluateSeries!(real x, real metafunction!(real y, int n) term)
 *
 * Evaluate a power series at compile time.
 *
 * Given a metafunction of the form
 *  real term!(real y, int n),
 * which gives the nth term of a convergent series at the point y
 * (where the first term is n==1), and a real number x,
 * this metafunction calculates the infinite sum at the point x
 * by adding terms until the sum doesn't change any more.
 */
template evaluateSeries(real x, alias term, int n=1, real sumsofar=0.0)
{
  static if (n>1 && sumsofar == sumsofar + term!(x, n+1)) {
     const real evaluateSeries = sumsofar;
  } else {
     const real evaluateSeries = evaluateSeries!(x, term, n+1, sumsofar + term!(x, n));
  }
}

/*** Calculate atan(x) at compile time.
 *
 * Uses the Maclaurin formula
 *  atan(z) = z - z^3/3 + Z^5/5 - Z^7/7 + ...
 */
template atan(real z)
{
    const real atan = evaluateSeries!(z, atanTerm);
}

template atanTerm(real x, int n)
{
    const real atanTerm =  (n & 1 ? 1 : -1) * pow!(x, 2*n-1)/(2*n-1);
}

/// Machin's formula for pi
/// pi/4 = 4 atan(1/5) - atan(1/239).
pragma(msg, "PI = " ~ fcvt!(4.0 * (4*atan!(1/5.0) - atan!(1/239.0))) );

2
Sayangnya, garis singgung arctangents didasarkan pada pi, agak membatalkan perhitungan ini.
Grant Johnson

14

Versi ini (dalam Delphi) tidak ada yang istimewa, tetapi setidaknya lebih cepat daripada versi yang diposting Nick Hodge di blog-nya :). Di komputer saya, dibutuhkan sekitar 16 detik untuk melakukan satu miliar iterasi, memberikan nilai 3,14159265 25879 (bagian yang akurat dicetak tebal).

program calcpi;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  start, finish: TDateTime;

function CalculatePi(iterations: integer): double;
var
  numerator, denominator, i: integer;
  sum: double;
begin
  {
  PI may be approximated with this formula:
  4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 .......)
  //}
  numerator := 1;
  denominator := 1;
  sum := 0;
  for i := 1 to iterations do begin
    sum := sum + (numerator/denominator);
    denominator := denominator + 2;
    numerator := -numerator;
  end;
  Result := 4 * sum;
end;

begin
  try
    start := Now;
    WriteLn(FloatToStr(CalculatePi(StrToInt(ParamStr(1)))));
    finish := Now;
    WriteLn('Seconds:' + FormatDateTime('hh:mm:ss.zz',finish-start));
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

13

Dulu, dengan ukuran kata yang kecil dan operasi floating-point yang lambat atau tidak ada, kami biasa melakukan hal-hal seperti ini:

/* Return approximation of n * PI; n is integer */
#define pi_times(n) (((n) * 22) / 7)

Untuk aplikasi yang tidak memerlukan banyak presisi (video game, misalnya), ini sangat cepat dan cukup akurat.


11
Untuk penggunaan yang lebih akurat 355 / 113. Sangat akurat untuk ukuran angka yang terlibat.
David Thornley

Hanya ingin tahu: 22/7 adalah3 + 1/7
Agnius Vasiliauskas

13

Jika Anda ingin menghitung perkiraan nilai π (karena alasan tertentu), Anda harus mencoba algoritma ekstraksi biner. Bellard ini peningkatan BBP memberikan dilakukannya PI di O (N ^ 2).


Jika Anda ingin mendapatkan perkiraan nilai π untuk melakukan perhitungan, maka:

PI = 3.141592654

Memang, itu hanya perkiraan, dan tidak sepenuhnya akurat. Ini mati sedikit lebih dari 0,00000000004102. (empat sepuluh triliun, sekitar 4 / 10.000.000.000 ).


Jika Anda ingin melakukan matematika dengan π, dapatkan sendiri pensil dan kertas atau paket aljabar komputer, dan gunakan nilai tepat π, π.

Jika Anda benar-benar menginginkan formula, ini menyenangkan:

π = - i ln (-1)


Rumus Anda tergantung pada bagaimana Anda mendefinisikan ld di bidang kompleks. Itu harus non-berdekatan sepanjang satu garis dalam bidang kompleks, dan itu cukup umum untuk garis itu menjadi sumbu nyata negatif.
erikkallen

12

Metode Brent yang diposting di atas oleh Chris sangat bagus; Brent umumnya adalah raksasa di bidang aritmatika presisi arbitrer.

Jika semua yang Anda inginkan adalah digit ke-N, rumus BBP yang terkenal berguna dalam hex


1
Metode Brent tidak diposting oleh saya; itu diposting oleh Andrea, dan saya kebetulan orang terakhir yang mengedit posting. :-) Tapi saya setuju, postingan itu layak mendapat upvote.
Chris Jester-Young

1

Menghitung π dari area lingkaran :-)

<input id="range" type="range" min="10" max="960" value="10" step="50" oninput="calcPi()">
<br>
<div id="cont"></div>

<script>
function generateCircle(width) {
    var c = width/2;
    var delta = 1.0;
    var str = "";
    var xCount = 0;
    for (var x=0; x <= width; x++) {
        for (var y = 0; y <= width; y++) {
            var d = Math.sqrt((x-c)*(x-c) + (y-c)*(y-c));
            if (d > (width-1)/2) {
                str += '.';
            }
            else {
                xCount++;
                str += 'o';
            }
            str += "&nbsp;" 
        }
        str += "\n";
    }
    var pi = (xCount * 4) / (width * width);
    return [str, pi];
}

function calcPi() {
    var e = document.getElementById("cont");
    var width = document.getElementById("range").value;
    e.innerHTML = "<h4>Generating circle...</h4>";
    setTimeout(function() {
        var circ = generateCircle(width);
        e.innerHTML  = "<pre>" + "π = " + circ[1].toFixed(2) + "\n" + circ[0] +"</pre>";
    }, 200);
}
calcPi();
</script>


0

Algoritma Chudnovsky cukup cepat jika Anda tidak keberatan melakukan root kuadrat dan beberapa invers. Konvergen ke presisi ganda hanya dalam 2 iterasi.

/*
    Chudnovsky algorithm for computing PI
*/

#include <iostream>
#include <cmath>
using namespace std;

double calc_PI(int K=2) {

    static const int A = 545140134;
    static const int B = 13591409;
    static const int D = 640320;

    const double ID3 = 1./ (double(D)*double(D)*double(D));

    double sum = 0.;
    double b   = sqrt(ID3);
    long long int p = 1;
    long long int a = B;

    sum += double(p) * double(a)* b;

    // 2 iterations enough for double convergence
    for (int k=1; k<K; ++k) {
        // A*k + B
        a += A;
        // update denominator
        b *= ID3;
        // p = (-1)^k 6k! / 3k! k!^3
        p *= (6*k)*(6*k-1)*(6*k-2)*(6*k-3)*(6*k-4)*(6*k-5);
        p /= (3*k)*(3*k-1)*(3*k-2) * k*k*k;
        p = -p;

        sum += double(p) * double(a)* b;
    }

    return 1./(12*sum);
}

int main() {

    cout.precision(16);
    cout.setf(ios::fixed);

    for (int k=1; k<=5; ++k) cout << "k = " << k << "   PI = " << calc_PI(k) << endl;

    return 0;
}

Hasil:

k = 1   PI = 3.1415926535897341
k = 2   PI = 3.1415926535897931
k = 3   PI = 3.1415926535897931
k = 4   PI = 3.1415926535897931
k = 5   PI = 3.1415926535897931

0

Pendekatan yang lebih baik

Untuk mendapatkan output konstanta standar seperti pi atau konsep standar, pertama-tama kita harus menggunakan metode builtin yang tersedia dalam bahasa yang Anda gunakan. Ini akan mengembalikan nilai dengan cara tercepat dan terbaik. Saya menggunakan python untuk menjalankan cara tercepat untuk mendapatkan nilai pi.

  • variabel pi dari perpustakaan matematika . Perpustakaan matematika menyimpan pi variabel sebagai konstanta.

math_pi.py

import math
print math.pi

Jalankan skrip dengan utilitas waktu linux /usr/bin/time -v python math_pi.py

Keluaran:

Command being timed: "python math_pi.py"
User time (seconds): 0.01
System time (seconds): 0.01
Percent of CPU this job got: 91%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03
  • Gunakan metode arc cos matematika

acos_pi.py

import math
print math.acos(-1)

Jalankan skrip dengan utilitas waktu linux /usr/bin/time -v python acos_pi.py

Keluaran:

Command being timed: "python acos_pi.py"
User time (seconds): 0.02
System time (seconds): 0.01
Percent of CPU this job got: 94%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03

bbp_pi.py

from decimal import Decimal, getcontext
getcontext().prec=100
print sum(1/Decimal(16)**k * 
          (Decimal(4)/(8*k+1) - 
           Decimal(2)/(8*k+4) - 
           Decimal(1)/(8*k+5) -
           Decimal(1)/(8*k+6)) for k in range(100))

Jalankan skrip dengan utilitas waktu linux /usr/bin/time -v python bbp_pi.py

Keluaran:

Command being timed: "python c.py"
User time (seconds): 0.05
System time (seconds): 0.01
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.06

Jadi cara terbaik adalah dengan menggunakan metode builtin yang disediakan oleh bahasa karena mereka adalah yang tercepat dan terbaik untuk mendapatkan output. Dalam python gunakan math.pi

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.