Π değerini almanın en hızlı yolu nedir?


322

Kişisel bir meydan okuma olarak π değerini elde etmenin en hızlı yolunu arıyorum. Daha spesifik olarak, #definesabitleri kullanmayı M_PIveya içindeki sayıyı sabit kodlamayı içermeyen yollar kullanıyorum .

Aşağıdaki program bildiğim çeşitli yolları test ediyor. Satır içi montaj sürümü, teorik olarak, açıkça taşınabilir olmasa da, en hızlı seçenektir. Diğer sürümlerle karşılaştırmak için bir temel olarak ekledim. Testlerimde, yerleşik olarak, 4 * atan(1)sürüm GCC 4.2'de en hızlıdır, çünkü otomatik atan(1)olarak bir sabite katlanır . İle -fno-builtinbelirtilen, atan2(0, -1)sürüm en hızlı.

İşte ana test programı ( 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;
}

Ve fldpi.cyalnızca x86 ve x64 sistemleri için çalışacak satır içi montaj öğeleri ( ):

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

Ve test ettiğim tüm yapılandırmaları oluşturan bir derleme ( 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

Çeşitli derleyici bayrakları arasında test dışında (optimizasyonlar farklı olduğu için 32-bit'i 64-bit ile de karşılaştırdım), testlerin sırasını değiştirmeyi de denedim. Ama yine de, atan2(0, -1)sürüm her seferinde hala zirveye çıkıyor.


38
C ++ meta programlamasında bunu yapmanın bir yolu olmalı. Çalışma süresi gerçekten iyi olacak, ancak derleme zamanı olmayacak.
David Thornley

1
Neden atanmış (1) 'i M_PI kullanmaktan farklı buluyorsunuz? Sadece aritmetik işlemleri kullandıysanız neden bunu yapmak istediğinizi anlıyorum, ama atan ile bu noktayı göremiyorum.
erikkallen

9
Soru şu: neden olur değil bir sabiti kullanmak istiyor? örneğin bir kütüphane tarafından mı yoksa kendiniz mi tanımladınız? Computing Pi, CPU döngüleri israfıdır, çünkü bu sorun günlük hesaplamalar için gerekenden çok daha fazla sayıda önemli basamağa tekrar tekrar çözülmüştür
Tilo

2
@ HopelessN00b Konuştuğum İngilizce lehçesinde, "optimizasyon" bir "z" ile değil, "zed", BTW, "zee" ;-) olarak telaffuz edilir) ile yazılır . (İnceleme geçmişine bakarsanız, bu tür bir düzenlemeyi ilk kez geri almak zorunda
kalmadım

Yanıtlar:


205

Monte Carlo yöntemi değil Uygun ölçülerde tarafından değil, en hızlı değil, uzun bir atış, açıkça, belirtildiği gibi, bazı büyük kavramlar geçerlidir ama öyle. Ayrıca, her şey ne tür bir doğruluk aradığınıza bağlıdır. Bildiğim en hızlı the rakamları sabit kodlu olanı. Pi ve Pi'ye baktığımızda [PDF] , birçok formül var.

İşte hızlı bir şekilde yakınlaşan bir yöntem - yineleme başına yaklaşık 14 basamak. Mevcut en hızlı uygulama olan PiFast , bu formülü FFT ile birlikte kullanıyor. Kod açık olduğu için sadece formülü yazacağım. Bu formül neredeyse Ramanujan tarafından bulundu ve Chudnovsky tarafından keşfedildi . Aslında sayının birkaç milyar basamağını nasıl hesapladı - bu yüzden göz ardı etmek için bir yöntem değil. Formül hızla taşacak ve faktöriyelleri böldüğümüz için, terimleri kaldırmak için bu tür hesaplamaları ertelemek avantajlı olacaktır.

resim açıklamasını buraya girin

resim açıklamasını buraya girin

nerede,

resim açıklamasını buraya girin

Aşağıda Brent-Salamin algoritması verilmiştir . Wikipedia, a ve b "yeterince yakın" olduğunda (a + b) ² / 4t'nin π'nın bir tahmini olacağını belirtmektedir . "Yeterince yakın" ne anlama geldiğinden emin değilim, ancak testlerimden birinde, bir yineleme 2 basamak, iki tane 7 ve üçü 15 vardı, elbette bu iki katına çıktı, bu yüzden temsiline bağlı olarak bir hata olabilir ve gerçek hesaplama daha doğru olabilir.

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)

Son olarak, bazı pi golf (800 basamaklı) ne dersiniz? 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
İlkini kendiniz uygulamaya çalıştığınızı varsayarsak, sqr (k3) sorun olmaz mı? Tahmin etmeniz gereken irrasyonel bir sayı olacağından eminim (IIRC, tam sayı olmayan tüm kökler irrasyoneldir). Sonsuz hassasiyet aritmetiği kullanıyorsanız, ancak bu kare kök bir anlaşma kırıcıysa, her şey oldukça basit görünüyor. İkincisi de bir sqrt içerir.
Bill K

2
deneyimlerime göre, 'yeterince yakın' genellikle bir taylor serisi yaklaşımı olduğu anlamına gelir.
Stephen

117

Bu programı gerçekten seviyorum, çünkü kendi alanına bakarak π'ya yaklaşıyor.

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
_ Yerine -F <00 || --F-OO-- koyarsanız takip etmek daha kolay olmalıdır :-)
Pat

1
veya _ ile "if (önceki karakter '-') {OO--;} F--;"
FryGuy

6
0.25 burada yazdırır.
Johannes Schaub - litb

8
Bu program 1998'de harikaydı, ancak kırıldı çünkü modern ön işlemciler, bunun gibi şeylerin çalışmasını önlemek için makro genişletmelerin etrafına boşluklar eklemeyle daha liberaller. Maalesef bu bir kalıntı.
Chris Lutz

38
Geçiş --traditional-cppiçin cpp amaçlanan davranışı elde etmek.
Nietzche-jou

78

İşte lisede öğrendiğim pi hesaplamak için bir tekniğin genel bir açıklaması.

Bunu sadece paylaşıyorum, çünkü herkesin onu sonsuza kadar hatırlayabileceği kadar basit olduğunu düşünüyorum, ayrıca size "Monte-Carlo" yöntemleri kavramını öğretiyor - ki bunlar hemen görünmeyen cevaplara ulaşmanın istatistiksel yöntemleri rastgele süreçlerle çıkarılabilir.

Bir kare çizin ve o karenin içine bir çeyrek daire (yarı dairenin dörtte biri) yazın (karenin kenarına eşit yarıçaplı bir çeyrek daire, böylece karenin mümkün olduğunca doldurulur)

Şimdi kareye bir dart atın ve nereye indiğini kaydedin - yani karenin herhangi bir yerinde rastgele bir nokta seçin. Tabii ki, meydanın içine indi, ama yarı dairenin içinde mi? Bu gerçeği kaydedin.

Bu işlemi birçok kez tekrarlayın - ve yarım daire içindeki nokta sayısının, atılan toplam sayıya oranı olduğunu göreceksiniz, bu oranı x olarak adlandırın.

Karenin alanı r çarpı r olduğundan, yarı dairenin alanının x çarpı r çarpı r (yani x çarpı r kare) olduğu sonucuna varabilirsiniz. Bu nedenle x çarpı 4 size pi verecektir.

Bu hızlı bir yöntem değildir. Ama bu bir Monte Carlo yöntemine güzel bir örnek. Etrafınıza bakarsanız, hesaplama becerileriniz dışındaki pek çok sorunun bu yöntemlerle çözülebileceğini görebilirsiniz.


2
Okuldaki bir java projesinde Pi'yi hesaplamak için kullandığımız yöntem budur. Sadece x, y koordinatlarını bulmak için bir randomizer kullandık ve daha fazla 'dart' geldiğimiz Pi'ye daha yakın attık.
Jeff Keslinke

55

Tamlık açısından, optimize edilmiş bir yapı için derleme zamanında PI yaklaşıklığını hesaplayacak ve tek bir değere satırlanacak bir C ++ şablon sürümü.

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

I> 10 için, optimize edilmiş derlemeler yavaş olabilir, aynı şekilde optimize edilmemiş çalışmalar için de. 12 tekrar için değer () (hatırlama yokluğunda) için yaklaşık 80 bin çağrı olduğuna inanıyorum.


Bunu çalıştırın ve "pi ~ 3.14159265383" olsun
maxwellb

5
9dp'ler için bu doğru. Bir şeye itiraz ediyor musunuz yoksa sadece bir gözlem mi yapıyorsunuz?
jon-hanson

burada PI hesaplamak için kullanılan algoritmanın adı nedir?
Sebastião Miranda

1
@ sebastião-miranda Leibniz'in formülü , ortalama hızlanma ile yakınsamayı artırır. pi_calc<0, J>birbirini izleyen her terimi formülden pi_calc<I, J>hesaplar ve uzman olmayanlar ortalamayı hesaplar.
jon-hanson

43

Jonathan ve Peter Borwein ( Amazon'da mevcut ) tarafından 'Pi ve AGM'nin hesaplanması için hızlı yöntemlere adanmış (diğer şeylerin yanı sıra) aslında bir kitap var .

AGM ve ilgili algoritmaları biraz çalıştım: oldukça ilginç (bazen önemsiz olmasa da).

\ Pi hesaplamak için en modern algoritmaları uygulamak için, çok duyarlıklı bir aritmetik kitaplığına ( GMP oldukça iyi bir seçimdir, ancak en son kullandığımdan beri bir süredir).

En iyi algoritmaların zaman karmaşıklığı O (M (n) günlüğü (n)) 'dir, burada M (n) iki n-bit tamsayısının çarpımı için zaman karmaşıklığıdır (M (n) = O (n log (n) log (log (n))), genellikle \ pi basamaklarını hesaplarken ihtiyaç duyulan FFT tabanlı algoritmalar kullanarak ve böyle bir algoritma GMP'de uygulanır).

Algoritmaların arkasındaki matematik önemsiz olmasa da, algoritmaların kendileri genellikle birkaç sahte kod satırıdır ve bunların uygulanmaları genellikle çok basittir (kendi çoklu hassasiyet aritmetiğinizi yazmamayı seçtiyseniz :-)).


42

Aşağıda , en az bilgi işlem çabasıyla bunun mümkün olan en hızlı şekilde nasıl yapılacağı tam olarak cevaplanmaktadır . Cevabı beğenmeseniz bile, PI değerini almanın gerçekten en hızlı yolu olduğunu itiraf etmelisiniz.

EN HIZLI Pi değerini almak için yoludur:

1) en sevdiğiniz programlama dilini seçin 2) Matematik kütüphanesini yükleyin 3) ve Pi'nin önceden tanımlanmış olduğunu bulun - kullanıma hazır!

Elinizde bir Matematik kütüphaneniz yoksa ..

İKİNCİ EN HIZLI yolu (daha evrensel çözüm) 'dir:

İnternette Pi'yi arayın, örneğin:

http://www.eveandersson.com/pi/digits/1000000 (1 milyon basamak .. kayan nokta hassasiyetiniz nedir?)

Veya burada:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

Veya burada:

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

Kullanmak istediğiniz hassas aritmetik için ihtiyacınız olan rakamları bulmak gerçekten hızlıdır ve bir sabit tanımlayarak değerli CPU zamanınızı boşa harcamayın.

Bu sadece mizahi bir cevap değil, gerçekte, eğer herhangi biri gerçek bir uygulamada Pi'nin değerini hesaplarsa .. bu CPU zamanının oldukça büyük bir israfı olurdu, değil mi? En azından bunu yeniden hesaplamaya çalıştığım için gerçek bir uygulama görmüyorum.

Sayın Moderatör: Lütfen OP'nin "PI değerini almanın en hızlı yolu" diye sorduğunu unutmayın.


Sevgili Tilo: OP'nin şunları söyledi: "Kişisel bir zorluk olarak as değerini elde etmenin en hızlı yolunu arıyorum. Daha spesifik olarak, M_PI gibi #define sabitleri kullanmayı içermeyen yollar kullanıyorum. veya numarayı sabit
Max

Sevgili @Max: Lütfen cevapladıktan sonra OP'nin orijinal sorusunu düzenlediğini unutmayın - bu benim hatam değil;) Benim çözümüm hala en hızlı yoldur ve problemi istenen herhangi bir kayan nokta hassasiyeti ve zarif bir CPU döngüsü ile çözer :)
Tilo

Üzgünüm, fark etmedim. Sadece bir düşünce, sabit kodlu sabitler pi hesaplamaktan daha az kesinlikli olmaz mıydı? Sanırım hangi dil olduğuna ve içerik oluşturucunun tüm rakamları koymaya ne kadar istekli olduğuna bağlı :-)
Max

1
Kahretsin Sevgili Tilo'yu
Max

27

BBP formül taban 2 (veya 16) 'de - - Eğer inci rakamı hesaplamak sağlar daha ilk önce N-1 basamak ile rahatsız etmeden :)


23

Pi'yi sabit olarak tanımlamak yerine daima kullanırım acos(-1).


2
cos (-1) veya acos (-1)? :-P (Sonuncusu) orijinal kodumdaki test durumlarından biri. Acos (-1) ile gerçekten aynı olan atan2 (0, -1) ile birlikte benim tercihim arasındadır, ancak acos genellikle atan2 açısından uygulanır), ancak bazı derleyiciler 4 * atan (1) için optimize eder !
Chris Jester-Young

21

Bu "klasik" bir yöntemdir, uygulanması çok kolaydır. Python'daki bu uygulama (en hızlı dil değil) bunu yapar:

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)

Daha fazla bilgiyi burada bulabilirsiniz .

Her neyse, pitonda pi'nin istediğiniz kadar kesin bir değerini elde etmenin en hızlı yolu:

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

İşte gmpy pi yöntemi için kaynak parçası, kod bu durumda yorum kadar yararlı olduğunu sanmıyorum:

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: Kes ve yapıştır ve girinti ile ilgili bazı sorunlar yaşadım, kaynağı burada bulabilirsiniz .



18

Bir yaklaşım kullanmak istiyorsanız, 355 / 1136 ondalık basamak için iyidir ve tamsayı ifadeleriyle kullanılabilir olma avantajına sahipse. Bugünlerde bu kadar önemli değil, "kayan nokta matematik yardımcı işlemcisi" herhangi bir anlam ifade etmedi, ama bir kez oldukça önemliydi.


18

Machin benzeri formülü kullanın

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.

Örneğin, Şema'da uygulanmıştır:

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


16

Çiftler ile:

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

Bu, bir çift doldurmaya yetecek kadar 14 ondalık basamağa kadar doğru olacaktır (yanlışlık muhtemelen ark teğetlerindeki ondalık sayıların geri kalanı kesilmiş olmasıdır).

Ayrıca Seth, 3.14159265358979323846 3 , 64 değil.


16

Pi tam olarak 3! [Prof. Frink (Simpsons)]

Şaka, ama burada bir C # (.NET-Framework gereklidir).

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

D ile derleme zamanında PI hesaplayın.

( DSource.org'dan kopyalandı )

/** 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
Ne yazık ki, teğetler arctangentler pi'ye dayanır, bu hesaplamayı biraz geçersiz kılar.
Grant Johnson

14

Bu sürüm (Delphi'de) özel bir şey değildir, ancak en azından blogunda yayınlanan Nick Hodge'dan daha hızlıdır :). Makinemde , 3.14159265 25879 değeri veren bir milyar yineleme yapmak yaklaşık 16 saniye sürüyor (doğru kısım kalın harflerle yazılmış).

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

Eski günlerde, küçük kelime boyutları ve yavaş veya var olmayan kayan nokta işlemleri ile, aşağıdaki gibi şeyler yapardık:

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

Çok fazla hassasiyet gerektirmeyen uygulamalar için (örneğin video oyunları), bu çok hızlıdır ve yeterince doğrudur.


11
Daha hassas kullanım için 355 / 113. İlgili sayıların boyutu için çok doğru.
David Thornley

: Sadece meraktan 22/7 olduğunu3 + 1/7
Agnius Vasiliauskas

13

Π değerinin yaklaşık değerini (herhangi bir nedenle) hesaplamak istiyorsanız , bir ikili ekstraksiyon algoritması denemelisiniz. Bellard'ın BBP'nin verdiği iyileşme O (N ^ 2) 'de PI yapar.


Hesaplamalar yapmak için π değerine yaklaşık bir değer elde etmek istiyorsanız , o zaman:

PI = 3.141592654

Elbette, bu sadece bir tahmin ve tamamen doğru değil. 0.00000000004102'den biraz daha uzakta. (dört on trilyonuncu, yaklaşık 4 / 10.000.000.000 ).


Eğer matematik yapmak istiyorsanΠ ile , kendinize bir kalem ve kağıt veya bir bilgisayar cebir paketi alın ve π kesin değerini π kullanın.

Gerçekten bir formül istiyorsanız, bu eğlenceli:

π = - i ln (-1)


Formülünüz, ln'yi karmaşık düzlemde nasıl tanımladığınıza bağlıdır. Karmaşık düzlemdeki bir çizgi boyunca bitişik olmamalıdır ve bu çizginin negatif gerçek eksen olması oldukça yaygındır.
erikkallen

12

Brent'in yukarıda Chris tarafından yayınlanan yöntemi çok iyi; Brent genellikle keyfi hassasiyetli aritmetik alanında bir devdir.

İstediğiniz tek şey N'inci basamak ise, ünlü BBP formülü onaltılı olarak yararlıdır


1
Brent yöntemi benim tarafımdan yayınlanmadı; Andrea tarafından yayınlandı ve ben sadece yazıyı düzenleyen son kişi oldum. :-) Ama katılıyorum, bu yazı bir oylamayı hak ediyor.
Chris Jester-Young

1

Daire alanından π hesaplanıyor :-)

<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

Chudnovsky algoritması bir kare kök ve bir çift ters çevirmeyi önemsemiyorsanız oldukça hızlıdır. Sadece 2 yineleme ile çifte kesinlik sağlar.

/*
    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;
}

Sonuçlar:

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

Daha İyi Yaklaşım

Pi veya standart kavramlar gibi standart sabitlerin çıktısını almak için , önce kullandığınız dilde mevcut yerleşik yöntemlerle gitmeliyiz. Bir değeri en hızlı ve en iyi şekilde döndürür. Pi değerini almak için en hızlı yolu çalıştırmak için python kullanıyorum.

  • matematik kütüphanesinin pi değişkeni . Matematik kütüphanesi, pi değişkenini sabit olarak saklar.

math_pi.py

import math
print math.pi

Komut dosyasını linux zaman yardımcı programıyla çalıştırın /usr/bin/time -v python math_pi.py

Çıktı:

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
  • Arc cos matematik yöntemini kullanın

acos_pi.py

import math
print math.acos(-1)

Komut dosyasını linux zaman yardımcı programıyla çalıştırın /usr/bin/time -v python acos_pi.py

Çıktı:

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))

Komut dosyasını linux zaman yardımcı programıyla çalıştırın /usr/bin/time -v python bbp_pi.py

Çıktı:

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

Bu yüzden en iyi yol, dil tarafından sağlanan yerleşik yöntemleri kullanmaktır, çünkü çıktı almak için en hızlı ve en iyilerdir. Python'da math.pi kullanın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.