关于性能:获得π值的最快方法是什么?

What is the fastest way to get the value of π?

我正在寻找获得π值的最快方法,作为个人挑战。更具体地说,我使用的方法不涉及使用#define常量(如M_PI),或者硬编码数字。

下面的程序测试了我所知道的各种方法。从理论上讲,内联汇编版本是最快的选择,但显然不可移植。我把它作为比较其他版本的基线。在我的测试中,使用内置的,4 * atan(1)版本在gcc 4.2上是最快的,因为它自动将atan(1)折叠成一个常量。在指定了-fno-builtin的情况下,atan2(0, -1)版本是最快的。

以下是主要的测试程序(pitimes.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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
", #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

1
2
3
4
5
6
7
double
fldpi()
{
    double pi;
    asm("fldpi" :"=t" (pi));
    return pi;
}

以及构建我正在测试的所有配置的构建脚本(build.sh):

1
2
3
4
5
6
7
8
9
10
#!/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)版本仍然每次都是最流行的。


如前所述,蒙特卡罗方法应用了一些伟大的概念,但显然,它不是最快的,不是一个长镜头,不是任何合理的措施。而且,这一切都取决于你在寻找什么样的准确度。我所知道的最快的π是数字硬编码的π。看看pi和pi[pdf],有很多公式。

这是一种快速收敛的方法——每次迭代大约14位数字。当前速度最快的应用程序pifast将此公式与fft一起使用。我只写公式,因为代码很简单。这个公式几乎是由拉马努扬和查德诺夫斯基发现的。这实际上是他计算这个数字数十亿位数的方法,所以这不是一个可以忽略的方法。这个公式会很快溢出,因为我们在除阶乘,所以推迟计算以删除项是有利的。

enter image description here

enter image description here

哪里,

enter image description here

下面是布伦特-萨拉敏算法。维基百科提到,当a和b"足够接近"时,(a+b)2/4t将是π的近似值。我不知道"足够接近"是什么意思,但是根据我的测试,一次迭代得到2位数字,两次得到7位,三次得到15位,当然这是双精度的,所以基于它的表示可能会有错误,真正的计算可能更准确。

1
2
3
4
5
6
7
8
9
10
11
12
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)

最后,来点皮球(800位)怎么样?160个字!

1
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);}


我真的很喜欢这个程序,因为它通过观察自己的面积来近似π。

国际奥委会1988年:韦斯特利。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f
",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}


这是我高中时学过的一种计算圆周率的方法的一般描述。

我只分享这一点,因为我认为它足够简单,任何人都可以无限期地记住它,而且它教给你"蒙特卡罗"方法的概念——这是获得答案的统计方法,这些方法似乎不能立即通过随机过程推导出来。

画一个正方形,在这个正方形内刻一个象限(半圆形的四分之一)(半径等于正方形边的一个象限,这样它就可以填充尽可能多的正方形)

现在把飞镖扔到广场上,记录它落在哪里——也就是说,在广场内的任何地方选择一个随机点。当然,它降落在正方形内,但它是在半圆形内吗?记录下这个事实。

重复这个过程很多次——你会发现半圆形内的点数与抛出的总数之比,称之为x。

因为正方形的面积是r乘以r,你可以推断出半圆形的面积是x乘以r乘以r(即x乘以r的平方)。因此x乘以4会得到π。

这不是一个快速使用的方法。但这是蒙特卡罗方法的一个很好的例子。如果你环顾四周,你可能会发现许多问题,否则你的计算能力之外的许多问题,可以通过这些方法来解决。


为了实现完整性,C++模板版本,对于优化的构建,将在编译时计算PI的近似值,并将内联到单个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#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,优化的构建速度可能很慢,对于非优化的运行也是如此。对于12个迭代,我相信有大约80k个对value()的调用(在没有Memoisation的情况下)。


实际上,有一本书(除其他外)专门介绍了乔纳森和彼得·博文(可在亚马逊上买到)的快速计算pi:pi和agm的方法。

我研究了很多AGM和相关的算法:它很有趣(尽管有时很重要)。

请注意,要实现大多数现代算法来计算pi,您需要一个多精度算法库(gmp是一个很好的选择,尽管我上次使用它已经有一段时间了)。

最佳算法的时间复杂度以o(m(n)log(n))为单位,其中m(n)是使用基于fft的算法将两个n位整数(m(n)=o(n log(n)log(n))相乘的时间复杂度,这种算法通常在计算pi的位数时需要,并且这种算法在gmp中实现)。

请注意,尽管算法背后的数学可能并不简单,但算法本身通常是几行伪代码,它们的实现通常非常简单(如果您选择不编写自己的多精度算法-)。


下面精确地回答了如何以尽可能快的方式做到这一点——用最少的计算工作量。即使你不喜欢这个答案,你也必须承认它确实是获得π值的最快方法。

获取pi值的最快方法是:

1)选择您最喜欢的编程语言2)加载其数学库3)并且发现pi已经在那里定义了——准备好使用了!

万一你手头没有数学图书馆……

第二个最快的方法(更通用的解决方案)是:

在Internet上查找pi,例如:

http://www.eveanersson.com/pi/digits/1000000(100万位)。你的浮点精度是多少?)

或在这里:

http://3.1415926535897932384664338327950288419716939933751055820974944592.com/

或在这里:

网址:http://en.wikipedia.org/wiki/pi

无论您想使用什么精度的算术,找到所需的数字都是非常快的,通过定义一个常量,您可以确保不会浪费宝贵的CPU时间。

这不仅是一个部分幽默的答案,而且在现实中,如果有人继续计算π在实际应用中的值的话。那将是对CPU时间的极大浪费,不是吗?至少我没有看到一个真正的应用程序试图重新计算这个。

尊敬的主持人:请注意,OP要求:"获取pi值的最快方法"


BBP公式允许您以2(或16)为基数计算第n个数字,而不必首先考虑前面的n-1个数字:)


我总是使用acos(-1),而不是将pi定义为常量。


如果这篇文章是真的,那么贝拉德创建的算法可能是最快的。他用台式电脑创造了2.7万亿个圆周率!

…他在这里发表了他的作品

干得好,贝拉德,你是个先锋!

http://www.theregister.co.uk/2010/01/06/very-long-pi/


刚刚遇到了一个完整性应该在这里的问题:

计算pi in piet

它有一个相当好的特性,可以提高精度,使程序更大。

以下是对语言本身的一些洞察


这是一个"经典"的方法,非常容易实现。这个实现是用Python(不是很快的语言)实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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"Costant pi: %.40f" % pi
print"Difference: %.40f" % abs(calc-pi)
print"Time elapsed: %s" % repr(t)

您可以在这里找到更多信息。

无论如何,在python中获得精确的pi值的最快方法是:

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

下面是gmpy pi方法的源代码,我认为在这种情况下,代码不如注释有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
static char doc_pi[]="\
pi(n): returns pi with n bits of precision in an mpf object
\
";

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

编辑:我对剪切、粘贴和识别有一些问题,无论如何,你可以在这里找到源代码。


如果说"最快"是指键入代码最快,那么下面是golfscript解决方案:

1
;''6666,-2%{2+.2/@*\/10.3??2*+}*`1000<~\;

使用类似机器的公式

1
2
3
4
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}
ight) ;], for you TeX the World people.

在方案中实现,例如:

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


如果您愿意使用近似值,355 / 113对于6位十进制数字是很好的,并且具有可用于整数表达式的附加优势。这并不像"浮点数学协处理器"那样重要,但它曾经非常重要。


用d计算编译时的pi。

(从dsource.org复制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/** 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))) );


圆周率正好是3![弗里克教授(辛普森一家)]

开玩笑,但这里有一个C(.NET框架是必需的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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;
        }
    }
}

双打:

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

这将精确到小数点后14位,足以填充一个双精度(不精确可能是因为弧切线中的其余小数被截断)。

还有塞思,是3.141592653589793238463,不是64。


这个版本(在Delphi中)并没有什么特别之处,但它至少比NickHodge在他的博客上发布的版本快。在我的机器上,大约需要16秒来进行10亿次迭代,得到的值为3.1415926525879(精确部分用粗体表示)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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.

如果你想计算π的近似值(出于某种原因),你应该尝试一种二进制提取算法。Bellard对BBP的改进给出了do pi in o(n^2)。

如果你想得到π的近似值来进行计算,那么:

1
PI = 3.141592654

当然,这只是一个近似值,并不完全准确。它关闭了0.00000000004102多一点。(四个十万亿分之一,约4/1000000000)。

如果你想用π做数学,那就给自己买一支铅笔和纸或者一个计算机代数包,然后用π的精确值π。

如果你真的想要一个公式,这个很有趣:

π=I LN(- 1)


在过去的日子里,我们使用小单词大小和缓慢或不存在的浮点运算,我们曾经做过这样的事情:

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

对于不需要太高精度的应用程序(例如,视频游戏),这是非常快的,并且足够精确。


上面克里斯发布的布伦特方法非常好;布伦特通常是任意精度算术领域的巨人。

如果你只想要第n个数字,著名的BBP公式在十六进制中有用


从圆面积计算π:—)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<input id="range" type="range" min="10" max="960" value="10" step="50" oninput="calcPi()">




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 +="
";
    }
    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  ="[cc]" +"π =" + circ[1].toFixed(2) +"
" + circ[0] +"

;},200);}钙();< /代码>


更好的方法

要获得标准常量(如pi或标准概念)的输出,我们首先应该使用您正在使用的语言提供的内置方法。它将以最快的方式和最好的方式返回值。我正在使用python获取获取值pi的最快方法

  • 数学库的pi变量。数学库将变量pi存储为常量。

马蒂皮皮耶

1
2
import math
print math.pi

运行Linux /usr/bin/time -v python math_pi.py的时间实用程序脚本

输出:

1
2
3
4
5
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
  • 使用弧cos数学方法

阿克苏派

1
2
import math
print math.acos(-1)

运行Linux /usr/bin/time -v python acos_pi.py的时间实用程序脚本

输出:

1
2
3
4
5
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公式

BBPIPI.Py

1
2
3
4
5
6
7
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))

使用Linux /usr/bin/time -v python bbp_pi.py的时间实用程序运行脚本

输出:

1
2
3
4
5
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

所以最好的方法是使用语言提供的内置方法,因为它们是获得输出的最快和最好的方法。在python中使用math.pi