Функция для нахождения n-й цифры числа Пи

Я всегда хотел найти алгоритм, который делал бы это. Меня не волнует, насколько он медленный, лишь бы он мог вернуть энную цифру числа Пи:

ex:

size_t piAt(long long int n)
{
}

Желательно не использовать бесконечный ряд.

Если у кого-то есть функция или класс, который делает это, в C или C++, мне было бы очень интересно это увидеть.

Спасибо


person jmasterx    schedule 06.05.2011    source источник
comment
Это похоже на точную копию stackoverflow.com/questions/2654749/how-is-pi-calculated (ваш собственный вопрос годичной давности). Есть ли какая-то причина, по которой вы снова задали новый вопрос?   -  person Mu Mind    schedule 06.05.2011
comment
@ Му Помни, это была формула, но теперь я прошу о реализации.   -  person jmasterx    schedule 06.05.2011
comment
@jmasterx см. Вызов Baking-Pi - понимание и улучшение, особенно пункт #3 в моем ответе ...   -  person Spektre    schedule 15.03.2016
comment
Разве вы не хотите, чтобы это возвращало int или unsigned int, а не size_t?   -  person Tom Karzes    schedule 19.03.2016


Ответы (3)


Это замечательное решение показывает, как вычислить Nth цифра π за время O(N) и пространство O(log·N), и сделать это без необходимости вычислять все предшествующие цифры.

О, и это в шестнадцатеричном формате.

Если вы не хотите этого делать, вы можете легко сделать это из оболочки:

% perl -Mbignum=bpi -wle 'print bpi(20)'
3.1415926535897932385

% perl -Mbignum=bpi -wle 'print bpi(50)'
3.1415926535897932384626433832795028841971693993751

% perl -Mbignum=bpi -wle 'print bpi(200)'
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303820

% perl -Mbignum=bpi -wle 'print bpi(1000)'
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199
person tchrist    schedule 06.05.2011
comment
Это идеально, но я ищу реализацию, так как не понимаю формулу, особенно для k = 0 до бесконечности... - person jmasterx; 06.05.2011
comment
@Milo: у вас есть конкретный вопрос или проблема с тем, как вы пытаетесь это реализовать? Это не writemeaprogram.com - person Wooble; 06.05.2011
comment
Не могли бы вы написать, как бы вы использовали формулу, где n, например, 5? Я просто не уверен, как использовать формулу. - person jmasterx; 06.05.2011
comment
@Milo: если вы не понимаете формулы и не можете программировать, то вы ничему из этого не учитесь и, по-видимому, имеете практическую потребность: возможно, ее можно удовлетворить, прочитав значение числа пи из файла ( вы можете скачать их из сети — например, piday.org/million.php), и индексация на интересующей вас позиции? Сэкономите много времени на вычисления.... ;-) - person Tony Delroy; 06.05.2011
comment
Нет, у меня нет от этого практического применения, я бы использовал для этого статическую таблицу поиска, меня просто интересует программа, которая может вычислить энную цифру числа пи. - person jmasterx; 06.05.2011
comment
@Milo: Ответ обновлен. Модуль Perl bignum имеет функцию bpi(N), которая возвращает точность от π до N цифр. - person tchrist; 06.05.2011
comment
Вычисление N-й цифры в базе 16 сильно отличается от вычисления N-й цифры в базе 10. Переход от одного к другому имеет нетривиальный большой-0 как во времени, так и в пространстве! - person R.. GitHub STOP HELPING ICE; 06.05.2011
comment
@TonyK: Немного подумав, я удалил свой комментарий. - person datenwolf; 18.01.2015
comment
Этот ответ не имеет ничего общего с вопросом ОП; ссылка описывает, как найти N-ю цифру по основанию 16, тогда как вопрос подразумевает основание 10. - person M.M; 23.08.2016
comment
Все, кто находит эту страницу, находятся здесь по ОДНОЙ ПРИЧИНЕ: у них есть практическая потребность. - person Lennart Rolland; 24.03.2017
comment
О, и это в шестнадцатеричном формате. смш - person Viliami; 23.05.2017

Вот решение Саймона Плуффа, закодированное Фабрисом Белларом:

   /*
     * Computation of the n'th decimal digit of \pi with very little memory.
     * Written by Fabrice Bellard on January 8, 1997.
     * 
     * We use a slightly modified version of the method described by Simon
     * Plouffe in "On the Computation of the n'th decimal digit of various
     * transcendental numbers" (November 1996). We have modified the algorithm
     * to get a running time of O(n^2) instead of O(n^3log(n)^3).
     * 
     * This program uses mostly integer arithmetic. It may be slow on some
     * hardwares where integer multiplications and divisons must be done
     * by software. We have supposed that 'int' has a size of 32 bits. If
     * your compiler supports 'long long' integers of 64 bits, you may use
     * the integer version of 'mul_mod' (see HAS_LONG_LONG).  
     */

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

/* uncomment the following line to use 'long long' integers */
/* #define HAS_LONG_LONG */

#ifdef HAS_LONG_LONG
#define mul_mod(a,b,m) (( (long long) (a) * (long long) (b) ) % (m))
#else
#define mul_mod(a,b,m) fmod( (double) a * (double) b, m)
#endif

/* return the inverse of x mod y */
int inv_mod(int x, int y)
{
    int q, u, v, a, c, t;

    u = x;
    v = y;
    c = 1;
    a = 0;
    do {
    q = v / u;

    t = c;
    c = a - q * c;
    a = t;

    t = u;
    u = v - q * u;
    v = t;
    } while (u != 0);
    a = a % y;
    if (a < 0)
    a = y + a;
    return a;
}

/* return (a^b) mod m */
int pow_mod(int a, int b, int m)
{
    int r, aa;

    r = 1;
    aa = a;
    while (1) {
    if (b & 1)
        r = mul_mod(r, aa, m);
    b = b >> 1;
    if (b == 0)
        break;
    aa = mul_mod(aa, aa, m);
    }
    return r;
}

/* return true if n is prime */
int is_prime(int n)
{
    int r, i;
    if ((n % 2) == 0)
    return 0;

    r = (int) (sqrt(n));
    for (i = 3; i <= r; i += 2)
    if ((n % i) == 0)
        return 0;
    return 1;
}

/* return the prime number immediatly after n */
int next_prime(int n)
{
    do {
    n++;
    } while (!is_prime(n));
    return n;
}

int main(int argc, char *argv[])
{
    int av, a, vmax, N, n, num, den, k, kq, kq2, t, v, s, i;
    double sum;

    if (argc < 2 || (n = atoi(argv[1])) <= 0) {
    printf("This program computes the n'th decimal digit of \\pi\n"
           "usage: pi n , where n is the digit you want\n");
    exit(1);
    }

    N = (int) ((n + 20) * log(10) / log(2));

    sum = 0;

    for (a = 3; a <= (2 * N); a = next_prime(a)) {

    vmax = (int) (log(2 * N) / log(a));
    av = 1;
    for (i = 0; i < vmax; i++)
        av = av * a;

    s = 0;
    num = 1;
    den = 1;
    v = 0;
    kq = 1;
    kq2 = 1;

    for (k = 1; k <= N; k++) {

        t = k;
        if (kq >= a) {
        do {
            t = t / a;
            v--;
        } while ((t % a) == 0);
        kq = 0;
        }
        kq++;
        num = mul_mod(num, t, av);

        t = (2 * k - 1);
        if (kq2 >= a) {
        if (kq2 == a) {
            do {
            t = t / a;
            v++;
            } while ((t % a) == 0);
        }
        kq2 -= a;
        }
        den = mul_mod(den, t, av);
        kq2 += 2;

        if (v > 0) {
        t = inv_mod(den, av);
        t = mul_mod(t, num, av);
        t = mul_mod(t, k, av);
        for (i = v; i < vmax; i++)
            t = mul_mod(t, a, av);
        s += t;
        if (s >= av)
            s -= av;
        }

    }

    t = pow_mod(10, n - 1, av);
    s = mul_mod(s, t, av);
    sum = fmod(sum + (double) s / (double) av, 1.0);
    }
    printf("Decimal digits of pi at position %d: %09d\n", n,
       (int) (sum * 1e9));
    return 0;
}

И это работает:

C:\tcc>tcc -I libtcc libtcc/libtcc.def -run examples/pi.c 1
Decimal digits of pi at position 1: 141592653

C:\tcc>tcc -I libtcc libtcc/libtcc.def -run examples/pi.c 1000
Decimal digits of pi at position 1000: 938095257

http://bellard.org/pi/pi_n2/pi_n2.html

person Lehs    schedule 17.01.2015
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - person AbcAeffchen; 18.01.2015
comment
Это не дает ответа на вопрос. Чтобы подвергнуть критике или запросить разъяснения у автора, оставьте комментарий под его публикацией. - person dario; 18.01.2015
comment
@AbcAeffchen: я работаю над этим, но моя интерпретация документа еще не работает в моей программе. - person Lehs; 18.01.2015
comment
@king.code: Майло запрашивает функцию, которая дает энное десятичное число числа пи. В документе содержится алгоритм, который делает именно это, что было бы первым шагом к написанию программы на языке C. - person Lehs; 18.01.2015
comment
@king.code: ну, как я уже писал: я работаю над алгоритмом и опубликую его, как только он сработает. - person Lehs; 18.01.2015
comment
@Lehs, так что вы опубликуете, когда закончите. Взгляните на это. - person dario; 18.01.2015
comment
Оно работает! Хороший ответ. Я хотел бы измерить, насколько близка реализация к O (n²). - person Niklas R.; 14.03.2016

Некоторые из решений, включая это, фактически печатают несколько цифр, начиная с n-й цифры.

Существует прекрасное (и поразительно более быстрое, чем предыдущие ответы) решение по адресу http://numbers.computation.free.fr/Constants/Algorithms/pidec.cpp

Код нуждается только в модификации typedef int64 ModInt; строка для определения типа int64_t ModInt;

gcc pidec.cpp && time echo 20000 | ./a.out
Pidec, direct computation of decimal digits of pi at a given position n.
(http://numbers.computation.free.fr/Constants/constants.html for more details)
Enter n : Parameters : M=122, N=7094, M*N+M=872562
Series time : 0.17
Digits of pi after n-th decimal digit : 203856539
Total time: 0.68

real    0m0.691s
user    0m0.678s
sys 0m0.006s

Compilation finished at Wed Feb  3 13:34:48

Это на MacBook Pro начала 2015 года.

person brucer42    schedule 03.02.2021