Який найшвидший спосіб отримати значення π?


322

Я шукаю найшвидший спосіб отримати значення π як особисте завдання. Більш конкретно, я використовую способи, які не передбачають використання #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)версія все одно виходить на вершину щоразу.


38
Має бути спосіб це зробити в метапрограмуванні на C ++. Час виконання буде справді хорошим, але часу компіляції не буде.
Девід Торнлі

1
Чому ви вважаєте, що використання atan (1) відрізняється від використання M_PI? Я зрозумів би, чому ви хочете це робити, якщо ви використовували лише арифметичні операції, але з атаном я не бачу сенсу.
erikkallen

9
питання: чому б ви не хотіли використовувати констант? наприклад, або визначено бібліотекою, або ви самі? Обчислення Pi - це витрата циклів процесора, оскільки ця проблема вирішується знову і знову для ряду значущих цифр, набагато більших, ніж потрібно для щоденних обчислень
Tilo

2
@ HopelessN00b На діалекті англійською мовою я кажу, що "оптимізація" пишеться з "s", а не "z" (що вимовляється як "zed", BTW, а не "zee" ;-)). (Це не перший раз, коли мені доводилося відновлювати подібні зміни, якщо ви також подивитесь на історію відгуків.)
Chris Jester-Young

Відповіді:


205

Як згадувалося, метод Монте-Карло застосовує кілька чудових концепцій, але він, очевидно, не найшвидший, не з дальнього пострілу, не з розумної міри. Також все залежить від того, яку точність ви шукаєте. Найшвидший π, про який я знаю, - це той, з цифрами жорстко закодований. Дивлячись на Pi та Pi [PDF] , існує багато формул.

Ось метод, який швидко зближується - близько 14 цифр за ітерацію. PiFast , поточний найшвидший додаток, використовує цю формулу разом з FFT. Я просто напишу формулу, оскільки код простий. Цю формулу майже знайшов Рамануджан і відкрив Чудновський . Це насправді, як він підрахував кілька мільярдів цифр числа, тож це не спосіб знехтувати. Формула швидко переповниться, і, оскільки ми ділимо фактичні факти, тоді було б вигідно відкласти такі обчислення, щоб видалити терміни.

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

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

де,

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

Нижче наведено алгоритм Brent – ​​Salamin . У Вікіпедії зазначається, що коли 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);}

1
Якщо припустити, що ви намагаєтеся реалізувати перший самостійно, чи не буде sqr (k3) проблемою? Я впевнений, що це призведе до ірраціонального числа, яке вам доведеться оцінити (IIRC, всі корені, які не є цілими числами, нераціональні). Все інше виглядає досить прямо, якщо ви використовуєте нескінченну арифметику точності, але цей квадратний корінь є вимикачем угоди. Другий включає також sqrt.
Білл К

2
на мій досвід, "достатньо близько" зазвичай означає, що є наближення ряду Тейлора.
Стівен

117

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

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
Якщо ви заміните _ з -F <00 || --F-OO-- має бути легше слідувати :-)
Pat

1
або, якщо ви заміните _ на "if (попередній символ - '-') {OO--;} F--;"
FryGuy

6
він друкує 0,25 тут -.-
Йоганнес Шауб - ліб

8
Ця програма була чудовою у 1998 році, але вона була порушена, оскільки сучасні препроцесори є більш ліберальними та вставляють пробіли навколо макророзширень, щоб запобігти роботі подібних речей. На жаль, це реліквія.
Кріс Лутц

38
Перейдіть --traditional-cppдо cpp, щоб отримати бажану поведінку.
Ніцше-джу

78

Ось загальний опис методики обчислення пі, яку я вивчив у середній школі.

Я поділяю це лише тому, що думаю, що це досить просто, щоб хто-небудь міг його запам'ятати нескінченно, плюс це навчає вас концепції методів "Монте-Карло" - це статистичні методи пошуку відповідей, які не відразу здаються виводиться через випадкові процеси.

Накресліть квадрат і впишіть всередину цього квадрата (одну чверть півкола) (квадрант із радіусом, рівним стороні квадрата, щоб він заповнив якомога більше площі)

Тепер киньте дротик на площу та запишіть, де вона приземляється - тобто вибирайте випадкову точку в будь-якому місці всередині квадрата. Звичайно, він приземлився всередині площі, але чи всередині півколу? Зафіксуйте цей факт.

Повторіть цей процес багато разів - і ви виявите, що існує відношення кількості точок всередині півколу проти загальної кількості кинутої, називайте це відношення x.

Оскільки площа квадрата r разів r, то можна зробити висновок, що площа напівколу є х разів r разів r (тобто x разів r квадрат). Отже, x x 4 дасть вам pi.

Це не швидкий метод використання. Але це приємний приклад методу Монте-Карло. А якщо озирнутися навколо, ви можете виявити, що багато проблем в іншому випадку поза вашими обчислювальними навичками можна вирішити такими методами.


2
Це метод, який ми використовували для обчислення Pi в проекті java в школі. Щойно використаний рандомізатор, щоб придумати координати x, y і тим більше "дротиків", які ми підкинули ближче до Пі, ми прийшли.
Джефф Кеслінке

55

В інтересах повноти, версія шаблону C ++, яка для оптимізованої збірки обчислить наближення PI під час компіляції і вкладе в єдине значення.

#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 ітерацій існує близько 80 тис. Дзвінків до значення () (за відсутності запам'ятовування).


Я запускаю це і отримую "pi ~ 3.14159265383"
maxwellb

5
Ну, це точно до 9dp. Ви чи заперечуєте проти чогось чи просто робите зауваження?
jon-hanson

як називається алгоритм, що використовується тут для обчислення PI?
Себастьян Міранда

1
@ sebastião-miranda Формула Лейбніца , із усередненим прискоренням покращує конвергенцію. pi_calc<0, J>обчислює кожен наступний член за формулою, а неспеціалізований pi_calc<I, J>обчислює середнє значення.
jon-hanson

43

Насправді є ціла книга, присвячена (серед іншого) швидким методам обчислення \ pi: "Пі та АГМ" Джонатана та Пітера Борвейна ( доступна на Amazon ).

Я вивчив AGM та пов'язані з ним алгоритми зовсім небагато: це досить цікаво (хоча іноді і нетривіально).

Зауважте, що для реалізації більшості сучасних алгоритмів для обчислення \ pi вам знадобиться багатоточна арифметична бібліотека ( GMP - досить хороший вибір, хоча минулого часу я використав це).

Часова складність кращих алгоритмів знаходиться в O (M (n) log (n)), де M (n) - часова складність для множення двох n-бітних цілих чисел (M (n) = O (n log (n) log (log (n))) з використанням алгоритмів на основі FFT, які зазвичай потрібні при обчисленні цифр \ pi, і такий алгоритм реалізований в GMP).

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


42

Нижче наведено конкретні відповіді, як це зробити найшвидшим способом - з найменшими зусиллями в обчислювальній роботі . Навіть якщо відповідь вам не подобається, ви повинні визнати, що це дійсно найшвидший спосіб отримати значення PI.

ШВИДКИЙ спосіб отримати значення Пі:

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

Якщо у вас немає під рукою бібліотеки математики ..

ДРУГИЙ ШВИДКИЙ спосіб (більш універсальне рішення) є:

шукайте Pi в Інтернеті, наприклад тут:

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

або тут:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

або тут:

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

Дійсно знайти потрібні цифри для будь-якої точної арифметики, яку ви хотіли б використовувати, і визначивши константу, ви можете переконатися, що ви не витрачаєте дорогоцінний час на процесор.

Це не лише частково жартівлива відповідь, але насправді, якщо хтось піде вперед і обчислить значення Pi в реальному застосуванні .., це було б досить великою тратою часу процесора, чи не так? Принаймні, я не бачу реальної програми для спроби перерахувати це.

Шановний модератор: зверніть увагу, що ОП запитала: "Найшвидший спосіб отримати значення PI"


Шановний Тіло: зауважте, що ОП заявила: "Я шукаю найшвидший спосіб отримати значення π як особистий виклик. Більш конкретно, я використовую способи, які не передбачають використання #define констант, таких як M_PI або жорстке кодування числа в .
Макс

Шановний @Max: зауважте, що ОП редагував своє первісне запитання після того, як я відповів на нього - навряд чи я винен;) Моє рішення все ще є найшвидшим способом, і вирішує проблему з будь-якою бажаною точністю з плаваючою комою та відсутністю циклу процесора елегантно :)
Тіло

Пробачте, я не зрозумів. Лише думка, чи не жорстко закодовані константи мають меншу точність, ніж обчислення пі? Я думаю, це залежить від мови, якою вона є і наскільки готовий творець ввести всі цифри :-)
Макс

1
Чорт я забув додати Шановний Тіло
Макс

27

Формула ВВР дозволяє обчислити п - ю цифру - в підставі 2 (або 16) - без того , щоб навіть турбуватися про попередніх N-1 цифр першим :)


23

Замість того, щоб визначати pi як постійну, я завжди використовую acos(-1).


2
cos (-1), або acos (-1)? :-P Це (останній) - один із тестових випадків мого оригінального коду. Він серед моїх переваг (поряд з atan2 (0, -1), який насправді такий же, як acos (-1), за винятком того, що acos зазвичай реалізується з точки зору atan2), але деякі компілятори оптимізують для 4 * atan (1) !
Кріс Єстер-Янг

21

Це "класичний" метод, дуже простий у здійсненні. Ця реалізація в 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;
}

EDIT: У мене виникли проблеми із вирізанням та вставкою та відступом. Ви можете знайти джерело тут .



18

Якщо ви готові скористатися наближенням, 355 / 113це добре для шести знаків після коми, і має додаткову перевагу в застосуванні з цілими виразами. Це не так важливо в ці дні, оскільки "співпроцесор математики з плаваючою комою" перестав мати будь-яке значення, але колись це було досить важливо.


18

Використовуйте формулу, подібну Махіну

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


16

З парними:

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

Це буде точно до 14 знаків після коми, достатньо, щоб заповнити дубль (неточність, мабуть, тому, що решта десяткових знаків у дугових дотичних обрізана).

Також Seth, це 3.14159265358979323846 3 , а не 64.


16

Пі рівно 3! [Проф. Frink (Сімпсони)]

Жарт, але ось ця у 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;
        }
    }
}

15

Обчисліть 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))) );

2
На жаль, тангенси - це арктангенти, засновані на pi, дещо недійсні для цього обчислення.
Грант Джонсон,

14

Ця версія (у 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.

13

Ще в давні часи, з невеликими розмірами слів і повільними або неіснуючими операціями з плаваючою комою, ми робили такі речі:

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

Для програм, які не вимагають великої точності (наприклад, відеоігри), це дуже швидко і досить точно.


11
Для більшої точності використання 355 / 113. Дуже точний за розміром залучених номерів.
Девід Торнлі

Просто з цікавості: 22/7 це3 + 1/7
Agnius Васіліаускас

13

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


Якщо ви хочете отримати наближення значення π, щоб зробити обчислення, то:

PI = 3.141592654

Зрозуміло, це лише наближення, і не зовсім точне. Вимкнено трохи більше 0,00000000004102. (Чотири десять-трильйонні, близько 4 / 10000000000 ).


Якщо ви хочете займатися математикою з π, то отримайте собі олівець і папір або пакет з комп'ютерної алгебри та використовуйте точне значення π, π.

Якщо ви дійсно хочете формули, ця забава:

π = - i ln (-1)


Ваша формула залежить від того, як ви визначаєте ln у складній площині. Він повинен бути безперервним по одній прямій у складній площині, і цією звичайною для цієї лінії є негативна реальна вісь.
erikkallen

12

Метод Брента, розміщений вище Крісом, дуже хороший; Взагалі Brent - гігант у галузі арифметики довільної точності.

Якщо все, що вам потрібно, - це N-та цифра, знаменита формула BBP корисна у шістнадцятковій формі


1
Метод Брента не був розміщений мною; її опублікував Андреа, і я щойно став останньою людиною, яка редагувала цю публікацію. :-) Але я згоден, що ця посада заслуговує на підсумок.
Кріс Єстер-Янг

1

Обчислення π від площі кола :-)

<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

Алгоритм Чудновського відбувається досить швидко, якщо ви не проти виконати квадратний корінь і пару перевернутих. Це сходиться до подвійної точності всього за 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

0

Кращий підхід

Щоб отримати вихід стандартних констант, таких як pi або стандартних понять, спершу слід перейти з вбудованими методами, доступними мовою, якою ви користуєтесь. Це поверне значення найшвидшим та найкращим способом. Я використовую python, щоб запустити найшвидший спосіб отримати значення pi.

  • 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
  • Використовуйте метод математики arc cos

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.