Какой самый быстрый способ получить значение π?

Я ищу самый быстрый способ получить значение π, как личный вызов. В частности, я использую способы, которые не связаны с использованием #define констант, таких как M_PI или жесткое кодирование числа в.

В приведенной ниже программе тестируются различные известные мне способы. Версия со встроенной сборкой теоретически является самым быстрым вариантом, хотя явно не переносима. Я включил его в качестве основы для сравнения с другими версиями. В моих тестах со встроенными модулями 4 * atan(1) версия была самой быстрой на GCC 4.2, потому что она автоматически сворачивает atan(1) в константу. С -fno-builtin указанием самая atan2(0, -1) быстрая версия.

Вот основная программа тестирования ( 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;
}

И встроенный сборщик ( fldpi.c ), который будет работать только для систем x86 и x64:

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

И сценарий сборки, который собирает все конфигурации, которые я тестирую ( build.sh ):

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

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

Помимо тестирования между различными флагами компилятора (я сравнивал 32-битную и 64-битную версии, потому что оптимизации разные), я также пробовал переключать порядок тестов. Но, тем не менее, atan2(0, -1) версия каждый раз выходит на первое место.

Ответов (22)

Решение

Как уже упоминалось, в методе Монте-Карло применены некоторые замечательные концепции, но он явно не самый быстрый, ни по большому счету, ни по какой-либо разумной мере. Кроме того, все зависит от того, какую точность вы ищете. Самая быстрая π, о которой я знаю, - это число с жестко закодированными цифрами. Глядя на Пи и Пи [PDF] , можно увидеть множество формул.

Вот метод, который быстро сходится - около 14 цифр на итерацию. PiFast , текущее самое быстрое приложение, использует эту формулу с БПФ. Я просто напишу формулу, так как код простой. Эта формула была почти найдена Рамануджаном и открыта Чудновским . На самом деле он подсчитал несколько миллиардов цифр числа именно так - так что это не метод, которым нельзя пренебрегать. Формула быстро переполнится, и, поскольку мы делим факториалы, было бы выгодно отложить такие вычисления, чтобы удалить члены.

введите описание изображения здесь

введите описание изображения здесь

куда,

введите описание изображения здесь

Ниже представлен алгоритм Брента – Саламина . Википедия упоминает, что когда a и b «достаточно близки», тогда (a + b) ² / 4t будет приближением к π. Я не уверен, что означает «достаточно близко», но, судя по моим тестам, одна итерация получила 2 цифры, две - 7, а три - 15, конечно, это с двойными числами, поэтому может быть ошибка, основанная на ее представлении и истинный расчет может быть более точным.

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 символов!

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

Формула BBP позволяет вам вычислить n-ю цифру - в базе 2 (или 16) - даже не заморачиваясь сначала с предыдущими цифрами n-1 :)

Следующие ниже дают точные ответы о том, как сделать это максимально быстро - с наименьшими вычислительными затратами . Даже если вам не нравится ответ, вы должны признать, что это действительно самый быстрый способ получить значение PI.

БЫСТРЫЙ способ получить значение Пи:

1) выберите свой любимый язык программирования 2) загрузите его математическую библиотеку 3) и обнаружите, что Pi уже определен там - готов к использованию!

Если у вас под рукой нет математической библиотеки ..

ВТОРОЙ БЫСТРЫЙ способ (более универсальное решение) является:

найдите Pi в Интернете, например здесь:

http://www.eveandersson.com/pi/digits/1000000 (1 миллион цифр .. какова ваша точность с плавающей запятой?)

или здесь:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

или здесь:

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

Очень быстро найти цифры, которые вам нужны для любой точной арифметики, которую вы хотели бы использовать, и, определив константу, вы можете быть уверены, что не тратите драгоценное время процессора.

Это не только отчасти юмористический ответ, но и в действительности, если бы кто-нибудь пошел дальше и вычислил значение Пи в реальном приложении ... это было бы довольно большой тратой времени ЦП, не так ли? По крайней мере, я не вижу настоящего приложения для повторного вычисления этого.

Уважаемый модератор: обратите внимание, что OP спросил: «Самый быстрый способ получить значение PI»

Это «классический» метод, очень простой в реализации. Эта реализация на Python (не самом быстром языке) делает это:

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)

Вы можете найти больше информации здесь .

В любом случае, самый быстрый способ получить точное значение числа пи в Python:

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

Вот фрагмент исходного кода для метода gmpy pi, я не думаю, что в этом случае код так же полезен, как комментарий:

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

РЕДАКТИРОВАТЬ: У меня были проблемы с вырезанием, вставкой и отступом, вы можете найти источник здесь .

С парными:

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

Это будет точно до 14 знаков после запятой, что достаточно для заполнения двойного числа (неточность, вероятно, связана с усечением остальных десятичных знаков в арктангенсах).

Также Сет, это 3,14159265358979323846 3 , а не 64.

Вычисление π из площади круга :-)

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

Лучший подход

Чтобы получить на выходе стандартные константы, такие как « пи» или стандартные концепции, мы должны сначала использовать встроенные методы, доступные на используемом вами языке. Он вернет значение самым быстрым и лучшим способом. Я использую python, чтобы максимально быстро получить значение числа пи.

  • переменная pi математической библиотеки . Математическая библиотека хранит переменную pi как константу.

math_pi.py

import math
print math.pi

Запускаем скрипт с утилитой времени linux /usr/bin/time -v python math_pi.py

Выход:

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
  • Используйте математический метод arccos

acos_pi.py

import math
print math.acos(-1)

Запускаем скрипт с утилитой времени linux /usr/bin/time -v python acos_pi.py

Выход:

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

Запускаем скрипт с утилитой времени linux /usr/bin/time -v python bbp_pi.py

Выход:

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

Алгоритм Чудновского довольно быстр, если вы не против вычисления квадратного корня и пары обратных чисел. Он сходится к двойной точности всего за 2 итерации.

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

Полученные результаты:

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

В основном ответ оптимизатора скрепок для C и гораздо более упрощенный:

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

double calc_PI(int K) {
    static const int A = 545140134;
    static const int B = 13591409;
    static const int D = 640320;
    const double ID3 = 1.0 / ((double) D * (double) D * (double) D);
    double sum = 0.0;
    double b = sqrt(ID3);
    long long int p = 1;
    long long int a = B;
    sum += (double) p * (double) a * b;
    for (int k = 1; k < K; ++k) {
        a += A;
        b *= ID3;
        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.0 / (12 * sum);
}

int main() {
    for (int k = 1; k <= 5; ++k) {
        printf("k = %i, PI = %.16f\n", k, calc_PI(k));
    }
}

Но для большего упрощения этот алгоритм использует формулу Чудновского, которую я могу полностью упростить, если вы действительно не понимаете код.

Резюме: мы получим число от 1 до 5 и добавим его в функцию, которую мы будем использовать для получения PI. Затем вам дается 3 числа: 545140134 (A), 13591409 (B), 640320 (D). Затем мы будем использовать D как double умножение себя 3 раза на другое double (ID3). Затем мы возьмем квадратный корень из ID3 в другой double (b) и присвоим 2 числа: 1 (p), значение B (a). Обратите внимание, что C не чувствителен к регистру. Тогда double (сумма) будет создана путем умножения значений p, a и b, все в double с. Затем цикл до тех пор, пока число, указанное для функции, не запустится и добавит значение A к a, значение b будет умножено на ID3, значение p будет умножено на несколько значений, которые, я надеюсь, вы поймете, а также разделится на несколько значений как хорошо. Сумма снова сложится на p, a и b, и цикл будет повторяться до тех пор, пока значение номера цикла не станет больше или равно 5. Позже сумма умножается на 12 и возвращается функцией, дающей нам результат ПИ.

Ладно, это было долго, но я думаю, вы это поймете ...

Вот общее описание техники вычисления числа Пи, которую я изучил в старшей школе.

Я разделяю это только потому, что считаю, что это достаточно просто, чтобы любой мог помнить это бесконечно долго, плюс он учит вас концепции методов «Монте-Карло», которые представляют собой статистические методы получения ответов, которые не сразу кажутся выводимые через случайные процессы.

Нарисуйте квадрат и впишите квадрант (четверть полукруга) внутри этого квадрата (квадрант с радиусом, равным стороне квадрата, чтобы он заполнял как можно большую часть квадрата)

Теперь бросьте дротик в квадрат и запишите, где он приземлится, то есть выберите случайную точку в любом месте квадрата. Конечно, он попал внутрь квадрата, но внутри ли он полукруга? Запишите этот факт.

Повторите этот процесс много раз - и вы обнаружите, что существует отношение количества точек внутри полукруга к общему количеству брошенных точек, назовите это отношение x.

Поскольку площадь квадрата равна r, умноженному на r, вы можете сделать вывод, что площадь полукруга равна x, умноженному на r, умноженному на r (то есть, x умножить на r в квадрате). Следовательно, x умножить на 4 даст вам число пи.

Это не быстрый способ использовать. Но это хороший пример метода Монте-Карло. И если вы посмотрите вокруг, вы можете обнаружить, что с помощью таких методов можно решить многие проблемы, не связанные с вашими вычислительными навыками.

Если под самым быстрым вы подразумеваете самый быстрый ввод кода, вот решение golfscript :

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

На самом деле существует целая книга, посвященная (среди прочего) быстрым методам вычисления \ pi: «Пи и AGM» Джонатана и Питера Борвейнов ( доступна на Amazon ).

Я немного изучил AGM и связанные с ним алгоритмы: это довольно интересно (хотя иногда и нетривиально).

Обратите внимание, что для реализации большинства современных алгоритмов вычисления \ pi вам понадобится арифметическая библиотека с множественной точностью ( GMP - неплохой выбор, хотя с тех пор, как я последний раз использовал его, прошло некоторое время).

Временная сложность лучших алгоритмов находится в O (M (n) log (n)), где M (n) - временная сложность умножения двух n-битных целых чисел (M (n) = O (n) log (n) log (log (n))) с использованием алгоритмов на основе БПФ, которые обычно необходимы при вычислении цифр \ pi, и такой алгоритм реализован в GMP).

Обратите внимание, что даже несмотря на то, что математика, лежащая в основе алгоритмов, может быть нетривиальной, сами алгоритмы обычно представляют собой несколько строк псевдокода, и их реализация обычно очень проста (если вы решили не писать свою собственную арифметику с высокой точностью :-)).

Мне очень нравится эта программа, потому что она приближает π, глядя на свою собственную область.

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

Рассчитайте PI во время компиляции с помощью D.

(Скопировано с 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))) );

В этой версии (в Delphi) нет ничего особенного, но она, по крайней мере, быстрее, чем версия, которую Ник Ходж опубликовал в своем блоге :). На моей машине, она занимает около 16 секунд , чтобы сделать миллиард итераций, что дает значение 3,14159265 25879 (точная часть выделены жирным шрифтом).

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.

В прежние времена, с маленькими размерами слов и медленными или отсутствующими операциями с плавающей запятой, мы делали такие вещи, как это:

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

Для приложений, которые не требуют большой точности (например, видеоигры), это очень быстро и достаточно точно.

Пи ровно 3! [Проф. Фринк (Симпсоны)]

Шутка, но вот один на C# (требуется .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;
        }
    }
}

Вместо того, чтобы определять пи как константу, я всегда использую acos(-1) .

Если вы хотите использовать приближение, 355 / 113 подходит для 6 десятичных цифр и имеет дополнительное преимущество, заключающееся в возможности использования с целочисленными выражениями. В наши дни это не так важно, потому что «математический сопроцессор с плавающей запятой» перестал иметь какое-либо значение, но когда-то это было очень важно.

In the interests of completeness, a C++ template version, which, for an optimised build, will compute an approximation of PI at compile time, and will inline to a single value.

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

Note for I > 10, optimised builds can be slow, likewise for non-optimised runs. For 12 iterations I believe there are around 80k calls to value() (in the absence of memoisation).

Если вы хотите вычислить приближение значения π (по какой-то причине), вам следует попробовать алгоритм двоичного извлечения. Беллар в улучшение BBP дает делает PI в O (N ^ 2).


Если вы хотите получить приблизительное значение π для проведения расчетов, тогда:

PI = 3.141592654

Конечно, это только приблизительное значение и не совсем точное значение. Это меньше, чем на 0,00000000004102. (четыре десять-триллионные, около 4 / 10000000000 ).


Если вы хотите заниматься математикой с π, возьмите карандаш и бумагу или пакет компьютерной алгебры и используйте точное значение π, π.

Если вам действительно нужна формула, это весело:

π = - i ln (-1)

Используйте формулу типа Машина

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.

Реализовано в схеме, например:

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