Cách nhanh nhất để có được giá trị của π là gì?


322

Tôi đang tìm cách nhanh nhất để đạt được giá trị của π, như một thách thức cá nhân. Cụ thể hơn, tôi đang sử dụng những cách không liên quan đến việc sử dụng các #definehằng số như M_PIhoặc mã hóa cứng số.

Chương trình dưới đây kiểm tra những cách khác nhau mà tôi biết. Về lý thuyết, phiên bản lắp ráp nội tuyến là tùy chọn nhanh nhất, mặc dù rõ ràng không thể mang theo được. Tôi đã đưa nó làm cơ sở để so sánh với các phiên bản khác. Trong các thử nghiệm của tôi, với các phần dựng sẵn, 4 * atan(1)phiên bản nhanh nhất trên GCC 4.2, vì nó tự động gập lại atan(1)thành một hằng số. Với -fno-builtinchỉ định, atan2(0, -1)phiên bản là nhanh nhất.

Đây là chương trình thử nghiệm chính ( 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;
}

Và công cụ lắp ráp nội tuyến ( fldpi.c) sẽ chỉ hoạt động cho các hệ thống x86 và x64:

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

Và một tập lệnh xây dựng xây dựng tất cả các cấu hình tôi đang thử nghiệm ( 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

Ngoài việc kiểm tra giữa các cờ trình biên dịch khác nhau (tôi cũng đã so sánh 32 bit với 64 bit vì các tối ưu hóa khác nhau), tôi cũng đã thử chuyển đổi thứ tự của các thử nghiệm xung quanh. Tuy nhiên, atan2(0, -1)phiên bản vẫn xuất hiện trên đầu mỗi lần.


38
Phải có một cách để làm điều đó trong siêu lập trình C ++. Thời gian chạy sẽ thực sự tốt, nhưng thời gian biên dịch sẽ không có.
David Thornley

1
Tại sao bạn lại cân nhắc sử dụng atan (1) khác với sử dụng M_PI? Tôi hiểu lý do tại sao bạn muốn làm điều này nếu bạn chỉ sử dụng các phép toán số học, nhưng với atan tôi không thấy được vấn đề.
erikkallen

9
Câu hỏi là: tại sao bạn không muốn sử dụng hằng số? ví dụ hoặc được xác định bởi một thư viện hoặc một mình? Tính toán Pi là một sự lãng phí chu kỳ CPU, vì vấn đề này đã được giải quyết nhiều lần đến một số chữ số có ý nghĩa lớn hơn nhiều so với mức cần thiết cho các tính toán hàng ngày
Tilo

2
@ HoplessN00b Trong phương ngữ tiếng Anh tôi nói, "tối ưu hóa" được đánh vần là "s", không phải là "z" (được phát âm là "zed", BTW, không phải "zee" ;-)). (Đây không phải là lần đầu tiên tôi phải hoàn nguyên loại chỉnh sửa này, nếu bạn nhìn vào lịch sử đánh giá.)
Chris Jester-Young

Câu trả lời:


205

Các phương pháp Monte Carlo , như đã đề cập, áp dụng một số khái niệm tuyệt vời nhưng nó là, rõ ràng, không phải là nhanh nhất, không phải bởi một shot dài, chứ không phải bằng bất kỳ biện pháp hợp lý. Ngoài ra, tất cả phụ thuộc vào loại chính xác mà bạn đang tìm kiếm. Số nhanh nhất mà tôi biết là số có chữ số được mã hóa cứng. Nhìn vào PiPi [PDF] , có rất nhiều công thức.

Đây là một phương pháp hội tụ nhanh chóng - khoảng 14 chữ số cho mỗi lần lặp. PiFast , ứng dụng nhanh nhất hiện tại, sử dụng công thức này với FFT. Tôi sẽ chỉ viết công thức, vì mã rất đơn giản. Công thức này gần như được tìm thấy bởi Ramanujan và được phát hiện bởi Chudnovsky . Đó thực sự là cách anh ta tính toán vài tỷ chữ số của con số - vì vậy đó không phải là một phương pháp để coi thường. Công thức sẽ tràn nhanh chóng và, vì chúng ta đang phân chia giai thừa, nên sẽ rất thuận lợi khi trì hoãn các tính toán như vậy để loại bỏ các điều khoản.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Ở đâu,

nhập mô tả hình ảnh ở đây

Dưới đây là thuật toán Brent Brent Salamin . Wikipedia đề cập rằng khi ab "đủ gần" thì (a + b) ² / 4t sẽ là một xấp xỉ của π. Tôi không chắc "đủ gần" nghĩa là gì, nhưng từ các thử nghiệm của tôi, một lần lặp có 2 chữ số, hai có 7 và ba có 15, tất nhiên điều này là gấp đôi, do đó, nó có thể có lỗi dựa trên biểu diễn của nó và các thực tính có thể là chính xác hơn.

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)

Cuối cùng, làm thế nào về một số golf pi (800 chữ số)? 160 ký tự!

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
Giả sử bạn đang cố gắng tự thực hiện cái đầu tiên, liệu sqr (k3) có thành vấn đề không? Tôi khá chắc chắn rằng nó sẽ kết thúc một số vô tỷ mà bạn sẽ phải ước tính (IIRC, tất cả các gốc không phải là số nguyên là không hợp lý). Mọi thứ khác trông khá đơn giản nếu bạn đang sử dụng số học chính xác vô hạn nhưng căn bậc hai đó là một công cụ thỏa thuận. Cái thứ hai bao gồm một sqrt là tốt.
Bill K

2
theo kinh nghiệm của tôi, 'đủ gần' thường có nghĩa là có một loạt xấp xỉ taylor liên quan.
Stephen

117

Tôi thực sự thích chương trình này, bởi vì nó xấp xỉ bằng cách nhìn vào khu vực riêng của nó.

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
Nếu bạn thay thế _ bằng -F <00 || --F-OO-- thì việc theo dõi sẽ dễ dàng hơn :-)
Pat

1
hoặc, nếu bạn thay thế _ bằng "if (ký tự trước là '-') {OO--;} F--;"
FryGuy

6
nó in 0,25 ở đây -.-
Julian Schaub - litb

8
Chương trình này rất tuyệt vào năm 1998, nhưng đã bị phá vỡ vì các bộ tiền xử lý hiện đại tự do hơn với việc chèn các khoảng trống xung quanh các bản mở rộng macro để ngăn những thứ như thế này hoạt động. Đó là một di tích, không may.
Chris Lutz

38
Chuyển --traditional-cppđến cpp để có được hành vi dự định.
Nietzche-jou

78

Đây là một mô tả chung về một kỹ thuật tính pi mà tôi đã học ở trường trung học.

Tôi chỉ chia sẻ điều này bởi vì tôi nghĩ nó đủ đơn giản để bất cứ ai cũng có thể nhớ nó, vô thời hạn, cộng với nó dạy cho bạn khái niệm về phương pháp "Monte-Carlo" - đó là những phương pháp thống kê để đi đến câu trả lời mà không xuất hiện ngay lập tức khấu trừ thông qua các quá trình ngẫu nhiên.

Vẽ một hình vuông và ghi một góc phần tư (một phần tư hình bán nguyệt) bên trong hình vuông đó (một góc phần tư có bán kính bằng cạnh của hình vuông, để nó lấp đầy càng nhiều hình vuông càng tốt)

Bây giờ ném phi tiêu vào quảng trường, và ghi lại nơi nó hạ cánh - nghĩa là chọn một điểm ngẫu nhiên ở bất cứ đâu trong quảng trường. Tất nhiên, nó đã hạ cánh bên trong quảng trường, nhưng nó có nằm trong vòng bán nguyệt không? Ghi lại sự thật này.

Lặp lại quá trình này nhiều lần - và bạn sẽ thấy có một tỷ lệ số điểm trong vòng bán nguyệt so với tổng số được ném, hãy gọi tỷ lệ này là x.

Vì diện tích của hình vuông là r lần r, nên bạn có thể suy ra rằng diện tích của hình bán nguyệt là x lần r lần r (nghĩa là x lần r bình phương). Do đó x lần 4 sẽ cho bạn pi.

Đây không phải là một phương pháp nhanh chóng để sử dụng. Nhưng đó là một ví dụ hay về phương pháp Monte Carlo. Và nếu bạn nhìn xung quanh, bạn có thể thấy rằng nhiều vấn đề khác ngoài các kỹ năng tính toán của bạn có thể được giải quyết bằng các phương pháp như vậy.


2
Đây là phương pháp chúng tôi đã sử dụng để tính toán Pi trong một dự án java ở trường. Chỉ cần sử dụng một bộ ngẫu nhiên để đưa ra tọa độ x, y và càng nhiều 'phi tiêu', chúng tôi càng ném gần Pi hơn.
Jeff Keslinke

55

Vì lợi ích của tính đầy đủ, một phiên bản mẫu C ++, với bản dựng được tối ưu hóa, sẽ tính toán xấp xỉ PI tại thời điểm biên dịch và sẽ nội tuyến theo một giá trị duy nhất.

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

Lưu ý đối với I> 10, các bản dựng được tối ưu hóa có thể chậm, tương tự đối với các lần chạy không được tối ưu hóa. Trong 12 lần lặp, tôi tin rằng có khoảng 80 nghìn cuộc gọi đến value () (trong trường hợp không có sự phân biệt).


Tôi chạy cái này và nhận được "pi ~ 3.14159265383"
maxwellb

5
Chà, chính xác là 9dp. Bạn đang phản đối một cái gì đó hoặc chỉ thực hiện một quan sát?
jon-hanson

tên của thuật toán được sử dụng ở đây để tính PI là gì?
Sebastião Miranda

1
Công thức của @ sebastião-miranda Leibniz , với khả năng tăng tốc trung bình cải thiện sự hội tụ. pi_calc<0, J>tính toán mỗi thuật ngữ liên tiếp từ công thức và tính không chuyên pi_calc<I, J>tính trung bình.
jon-hanson

43

Thực sự có cả một cuốn sách dành riêng (trong số những thứ khác) cho các phương pháp nhanh để tính toán \ pi: 'Pi và AGM', của Jonathan và Peter Borwein ( có sẵn trên Amazon ).

Tôi đã nghiên cứu AGM và các thuật toán liên quan khá nhiều: nó khá thú vị (mặc dù đôi khi không tầm thường).

Lưu ý rằng để triển khai hầu hết các thuật toán hiện đại để tính toán \ pi, bạn sẽ cần một thư viện số học đa năng ( GMP là một lựa chọn khá tốt, mặc dù đã lâu rồi tôi mới sử dụng nó).

Độ phức tạp thời gian của các thuật toán tốt nhất là trong O (M (n) log (n)), trong đó M (n) là độ phức tạp thời gian để nhân hai số nguyên n bit (M (n) = O (n (n) log (n) log (log (n))) bằng thuật toán dựa trên FFT, thường là cần thiết khi tính toán các chữ số của \ pi và thuật toán như vậy được triển khai trong GMP).

Lưu ý rằng mặc dù toán học đằng sau các thuật toán có thể không tầm thường, bản thân các thuật toán thường là một vài dòng mã giả và việc triển khai chúng thường rất đơn giản (nếu bạn chọn không viết số học bội số của riêng mình :-)).


42

Các câu trả lời sau đây chính xác làm thế nào để làm điều này theo cách nhanh nhất có thể - với nỗ lực tính toán ít nhất . Ngay cả khi bạn không thích câu trả lời, bạn phải thừa nhận rằng đó thực sự là cách nhanh nhất để có được giá trị PI.

Cách NHANH NHẤT để có được giá trị của Pi là:

1) chọn ngôn ngữ lập trình yêu thích của bạn 2) tải thư viện Toán học 3) và thấy rằng Pi đã được xác định ở đó - sẵn sàng để sử dụng!

Trong trường hợp bạn không có thư viện Toán trong tay ..

Cách NHANH NHẤT (giải pháp phổ quát hơn) là:

tra cứu Pi trên Internet, ví dụ ở đây:

http://www.eveandersson.com/pi/digits/1000000 (1 triệu chữ số .. độ chính xác của dấu phẩy động của bạn là gì?)

hoặc ở đây:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/.com/

hoặc ở đây:

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

Thật nhanh chóng để tìm các chữ số bạn cần cho bất kỳ số học chính xác nào bạn muốn sử dụng và bằng cách xác định một hằng số, bạn có thể đảm bảo rằng bạn không lãng phí thời gian CPU quý giá.

Đây không chỉ là một câu trả lời hài hước, mà trong thực tế, nếu có ai đi trước và tính giá trị của Pi trong một ứng dụng thực sự .. đó sẽ là một sự lãng phí khá lớn thời gian của CPU, phải không? Ít nhất tôi không thấy một ứng dụng thực sự để thử tính lại cái này.

Kính gửi người điều hành: xin lưu ý rằng OP đã hỏi: "Cách nhanh nhất để nhận giá trị PI"


Tilo thân mến: xin lưu ý rằng OP đã nói: "Tôi đang tìm cách nhanh nhất để đạt được giá trị của π, như một thách thức cá nhân. Cụ thể hơn, tôi đang sử dụng các cách không liên quan đến việc sử dụng các hằng số #define như M_PI hoặc mã hóa cứng số trong .
Tối đa

Kính gửi @Max: xin lưu ý rằng OP đã chỉnh sửa câu hỏi ban đầu của họ sau khi tôi trả lời - đó không phải là lỗi của tôi;) Giải pháp của tôi vẫn là cách nhanh nhất và giải quyết vấn đề với bất kỳ độ chính xác điểm nổi mong muốn nào và không có CPU quay vòng một cách thanh lịch :)
Tilo

Ôi xin lỗi, tôi đã không nhận ra. Chỉ cần một suy nghĩ, các hằng số mã hóa cứng có độ chính xác thấp hơn so với tính toán pi? Tôi đoán nó phụ thuộc vào ngôn ngữ của nó và mức độ sẵn sàng của người tạo để đặt tất cả các chữ số vào :-)
Tối đa

1
Chết tiệt, tôi quên thêm Tilo thân mến
Tối đa

27

Công thức BBP cho phép bạn tính chữ số thứ n - trong cơ sở 2 (hoặc 16) - mà không phải bận tâm đến các chữ số n-1 trước đó trước :)


23

Thay vì định nghĩa pi là hằng số, tôi luôn sử dụng acos(-1).


2
cos (-1), hay acos (-1)? :-P That (cái sau) là một trong những trường hợp thử nghiệm trong mã gốc của tôi. Đó là một trong những ưu tiên của tôi (cùng với atan2 (0, -1), thực sự giống với acos (-1), ngoại trừ acos thường được triển khai theo atan2), nhưng một số trình biên dịch tối ưu hóa cho 4 * atan (1) !
Chris Jester-Young

21

Đây là một phương pháp "cổ điển", rất dễ thực hiện. Việc thực hiện này trong python (không phải ngôn ngữ nhanh nhất) thực hiện nó:

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)

Bạn có thể tìm thêm thông tin ở đây .

Dù sao, cách nhanh nhất để có được giá trị chính xác như bạn muốn của pi trong python là:

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

Đây là đoạn nguồn cho phương pháp gmpy pi, tôi không nghĩ mã này hữu ích như nhận xét trong trường hợp này:

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: Tôi đã có một số vấn đề với cắt và dán và thụt, bạn có thể tìm thấy nguồn ở đây .


20

Nếu nhanh nhất bạn có nghĩa là nhanh nhất để nhập mã, thì đây là giải pháp golfscript :

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

18

Nếu bạn sẵn sàng sử dụng một xấp xỉ, 355 / 113tốt cho 6 chữ số thập phân và có thêm lợi thế là có thể sử dụng được với các biểu thức số nguyên. Điều đó không còn quan trọng trong những ngày này, vì "bộ đồng xử lý toán học dấu phẩy động" đã không còn ý nghĩa gì nữa, nhưng nó khá quan trọng một lần.


18

Sử dụng công thức giống 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.

Được triển khai trong Đề án, ví dụ:

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


16

Với đôi:

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

Điều này sẽ chính xác tới 14 chữ số thập phân, đủ để điền một số kép (sự không chính xác có lẽ là do phần còn lại của số thập phân trong các tiếp tuyến cung bị cắt cụt).

Ngoài ra Seth, đó là 3.14159265358979323846 3 , không phải 64.


16

Pi chính xác là 3! [Giáo sư Nháy mắt (Simpsons)]

Trò đùa, nhưng đây là một trong C # (Yêu cầu .NET-Framework).

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

Tính PI tại thời điểm biên dịch với D.

( Sao chép từ 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
Thật không may, tiếp tuyến là arctangents dựa trên pi, phần nào làm mất hiệu lực tính toán này.
Grant Johnson

14

Phiên bản này (trong Delphi) không có gì đặc biệt, nhưng ít nhất là nhanh hơn phiên bản Nick Hodge đăng trên blog của mình :). Trên máy của tôi, mất khoảng 16 giây để thực hiện một tỷ lần lặp, cho giá trị là 3,14159265 25879 (phần chính xác được in đậm).

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

Quay lại thời xưa, với kích thước từ nhỏ và các hoạt động dấu phẩy động chậm hoặc không tồn tại, chúng tôi đã từng làm những việc như thế này:

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

Đối với các ứng dụng không yêu cầu nhiều độ chính xác (ví dụ như trò chơi video), điều này rất nhanh và đủ chính xác.


11
Để sử dụng chính xác hơn 355 / 113. Rất chính xác cho kích thước của số liên quan.
David Thornley

Chỉ tò mò thôi: 22/73 + 1/7
Ag Pa Vasiliauskas

13

Nếu bạn muốn tính xấp xỉ giá trị của π (vì một số lý do), bạn nên thử thuật toán trích xuất nhị phân. Sự cải thiện BBP của Bellard mang lại PI trong O (N ^ 2).


Nếu bạn muốn lấy giá trị gần đúng của giá trị π để thực hiện các phép tính, thì:

PI = 3.141592654

Cấp, đó chỉ là một xấp xỉ, và không hoàn toàn chính xác. Nó tắt hơn một chút so với 0,0000000000004102. (bốn phần mười nghìn, khoảng 4 / 10.000.000.000 ).


Nếu bạn muốn làm toán với số π, thì hãy lấy cho mình một cây bút chì và giấy hoặc gói đại số máy tính và sử dụng giá trị chính xác của π, π.

Nếu bạn thực sự muốn một công thức, thì đây là một công thức thú vị:

π = - tôi ln (-1)


Công thức của bạn phụ thuộc vào cách bạn xác định ln trong mặt phẳng phức. Nó phải không tiếp giáp dọc theo một đường thẳng trong mặt phẳng phức và điều đó khá phổ biến đối với đường thẳng đó là trục thực âm.
erikkallen

12

Phương pháp của Brent được đăng ở trên bởi Chris là rất tốt; Brent nói chung là một người khổng lồ trong lĩnh vực số học chính xác tùy ý.

Nếu tất cả những gì bạn muốn là chữ số thứ N, công thức BBP nổi tiếng rất hữu ích trong hex


1
Phương pháp Brent không được đăng bởi tôi; nó được đăng bởi Andrea và tôi chỉ là người cuối cùng chỉnh sửa bài đăng. :-) Nhưng tôi đồng ý, bài đăng đó xứng đáng được upvote.
Chris Jester-Young

1

Tính số π từ diện tích hình tròn :-)

<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

Thuật toán Chudnovsky khá nhanh nếu bạn không thực hiện một căn bậc hai và một vài nghịch đảo. Nó hội tụ đến độ chính xác gấp đôi chỉ trong 2 lần lặp.

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

Các kết quả:

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

Cách tiếp cận tốt hơn

Để có được đầu ra của các hằng tiêu chuẩn như pi hoặc các khái niệm tiêu chuẩn, trước tiên chúng ta nên sử dụng các phương thức dựng sẵn có trong ngôn ngữ mà bạn đang sử dụng. Nó sẽ trả về một giá trị một cách nhanh nhất và tốt nhất. Tôi đang sử dụng python để chạy cách nhanh nhất để có được giá trị của pi.

  • biến pi của thư viện toán học . Thư viện toán lưu trữ biến pi là hằng số.

math_pi.py

import math
print math.pi

Chạy script với tiện ích thời gian của linux /usr/bin/time -v python math_pi.py

Đầu ra:

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
  • Sử dụng phương pháp toán học arc arc

acos_pi.py

import math
print math.acos(-1)

Chạy script với tiện ích thời gian của linux /usr/bin/time -v python acos_pi.py

Đầu ra:

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

Chạy script với tiện ích thời gian của linux /usr/bin/time -v python bbp_pi.py

Đầu ra:

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

Vì vậy, cách tốt nhất là sử dụng các phương thức dựng sẵn được cung cấp bởi ngôn ngữ vì chúng là cách nhanh nhất và tốt nhất để có được đầu ra. Trong python sử dụng math.pi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.