Liczba Pi

    -- Sebastian Pawlak


Liczba π, ludolfina, liczba rzeczywista, niewymierna, będąca stosunkiem długości obwodu koła do jego średnicy,
π≈

03.141592653589793238462643383279502884197169399375105820
974944592307816406286208998628034825342117067982148086513
282306647093844609550582231725359408128481117450284102701
938521105559644622948954930381964428810975665933446128475
648233786783165271201909145648566923460348610454326648213
393607260249141273724587006606315588174881520920962829254
091715364367892590360011330530548820466521384146951941511
609433057270365759591953092186117381932611793105118548074
462379962749567351885752724891227938183011949129833673362
440656643086021394946395224737190702179860943702770539217
176293176752384674818467669405132000568127145263560827785
771342757789609173637178721468440901224953430146549585371
050792279689258923542019956112129021960864034418159813629
774771309960518707211340999998372978049951059731732816096
318595024459455346908302642522308253344685035261931188171
010003137838752886587533208381420617177669147303598253490
428755468731159562863882353787593751957781857780532171226
80661300192787661119590921642

W 1674 roku Leibniz i Gregory wymyślili wzór na liczbę Pi:

Pi/4

Sumując kolejne wyrazy szeregu otrzymujemy coraz większe przybliżenie liczby Pi.

W jaki sposób dojść do powyższego wzoru?
Otóż, wyjdę z faktu, że π/4 = arctan(1).
Dalej, przychodzi z pomocą wzór Taylora. Podczas pracy nad programem wyznaczającym wartość Pi, korzystałem z książki "Rachunek różniczkowy i całkowy", tom 1, G.M. Fichtenholza. W tejże książce wyjaśniono dokładnie wzór Taylora oraz zagadnienie to poparto licznymi przykładami. Za pomocą wzoru Taylora, możliwe jest przedstawienie funkcji w postaci nieskończonego szeregu.

wzór Taylora

wyznaczając kolejne pochodne funkcji y = arctan(x) mamy:
pochodne

stąd:
arctan(x)

zatem:
arctan(1)

W praktyce wzór wywodzący się z arctan(1) nie jest pożyteczny, gdyż chcąc obliczyć Pi z dokładnością do dziesiątego miejsca po przecinku, należy zsumować około 5 miliardów wyrazów.


Znacznie szybciej dąży do Pi szereg uzyskany ze wzoru:
wzór

Szereg Taylora uzyskany na podstawie powyższego wzoru użyłem w moim programie.


Kod źródłowy pliku "liczba.c":

/**********************************************************
 * Wyznaczanie liczby Pi z nieskonczonego szeregu postaci *
 * pi/4 = 4*arctan(1/5) - arctan(1/239)                   *
 * Sebastian Pawlak                                       *
 **********************************************************/

/* Komentarz:
 * Algorytm nie jest zoptymalizowany. Nieoptymalnie napisane funkcje dodawania
 * duzych liczb (petla przechodzi po wszystkich cyfrach, nawet jesli bardziej
 * znaczace cyfry to same zera) powoduje, ze program zwalnia jesli
 * wywolamy go z duza wartoscia.
 */

#include <stdio.h>
#include <stdlib.h>
#define BAZA 10     /* podstawa systemu liczbowego */

long nCyfr;
char *liczba1, *pom1, *pom_1;
char *pom2,*pom_2;


/* Czysci dluga liczbe i umieszcza w niej dwucyfrowa liczbe.
 */
void nadajWartosc(char *liczba, char c1, char c2)
{
    long i;

    for (i = 0; i < nCyfr; i++)
        liczba[i] = 0;

     liczba[0] = c1;
     liczba[1] = c2;
}


/* Sumuje dwie dlugie liczby i wynik umieszcza w pierwszej.
 */
void suma(char *wynik, char *czynnik)
{
    long i;

    for (i = nCyfr - 1; i >= 0; i--) {
        wynik[i] += czynnik[i];
        if (wynik[i] >= BAZA) {
            wynik[i] -= BAZA;
            wynik[i - 1]++;
        }
    }
}


/* Odejmuje dwie dlugie liczby i wynik umieszcza w pierwszej.
 */
void roznica(char *wynik, char *odjemnik)
{
    long i;

    for (i = nCyfr - 1; i >= 0; i--) {
        wynik[i] -= odjemnik[i];
        if (wynik[i] < 0) {
            wynik[i] += BAZA;
            wynik[i - 1]--;
        }
    }
}


/* Iloczyn dlugiej liczby i liczby typu long int.
 */
void iloczyn(char *wynik, unsigned long czynnik)
{
    long i, prz, przenies = 0;

    for (i = nCyfr - 1; i >= 0; i--) {
        prz = (wynik[i] * czynnik + przenies) / BAZA;
        wynik[i] = (wynik[i] * czynnik + przenies) % BAZA;
        przenies = prz;
    }
}


/* Iloraz dlugiej liczby i liczby typu int.
 */
void iloraz(char *wynik, int dzielnik)
{
    long i, prz, przenies = 0;

    for (i = 0; i < nCyfr; i++) {
        prz = (wynik[i] + przenies * BAZA) % dzielnik;
        wynik[i] = (wynik[i] + przenies * BAZA) / dzielnik;
        przenies = prz;
    }
}


/* Operacja przypisania dlugiej liczby innej dlugiej liczbie.
 */
void kopiuj(char *wynik, char *zrodlo)
{
    long i;

    for (i = 0; i < nCyfr; i++)
        wynik[i] = zrodlo[i];
}


/* Koniec programu, gdy ulamek jest tak maly (np. 0.00000000001), ze w
 * dluzszej liczbie mieszcza sie tylko zera.
 */
int czyKoniec(char *liczba)
{
    long i;

    for (i = 0; i < nCyfr; i++)
        if (liczba[i])
            return (0);

    return (1);
}


/* Liczby dla tan(1/5) i dla tan(1/239) sa coraz mniejsze (np. 0.1, 0.000123,
 * 0.00000005763). Na podstawie ilosci zer mozna wywnioskowac, do ktorego
 * miejsca po przecinku nalezy wyswietlic liczbe Pi.
 * Jesli np. tan(1/5)=0.0001289 a tan(1/239)=0.00000573 to wiadomo, ze
 * na pozycjach, na ktorych wystepuja zera nie pojawi sie juz zadna inna
 * cyfra, dlatego dla powyzszego przykladu mozna wypiasc 3 miejsca po
 * przecinku dla Pi.
 *
 * Funkcja okresla ile zer po przecinku wystepuje w danej duzej liczbie.
 * Funkcja wywolywana jest dla liczby przechowujacej wartosci tan(1/5), gdyz
 * ilosc zer po przecinku najwolniej rosnie.
 */
long liczbaMiejsc(char *liczba)
{
    long i;

    for (i = 0; i < nCyfr; i++)
        if (liczba[i])    /* != 0 */
            return (i);

    return (nCyfr - 1);
}


/* Wyswietla dluga liczbe.
 */
void wypiszLiczbe(char *liczba, int j, int k)
{
    long i;

    if (j < 2)
        printf ("%c%c.", liczba[0] + '0', liczba[1] + '0');
    else
        for (i = j; i < k; i++)
            printf ("%c", liczba[i] + '0');

    fflush (stdout);
}


/* Glowna funkcja obliczeniowa.
 */
void obliczenia(void)
{
    long k = 1;
    long il_m = 0;
    int p5_2 = 5 * 5;
    int p239_2 = 239 * 239;

    nadajWartosc(liczba1, 1, 6);
    iloraz(liczba1, 5);
    nadajWartosc(pom1, 0, 4);
    iloraz(pom1, 239);
    kopiuj(pom2, pom1);
    kopiuj(pom1, liczba1);
    roznica(liczba1, pom2);

    do {
        iloraz(pom1, p5_2);
        kopiuj(pom_1, pom1);
        iloraz(pom_1, 2 * k + 1);
        if (k % 2 != 0)
            roznica(liczba1, pom_1);
        else
            suma(liczba1, pom_1);

        iloraz(pom2, p239_2);
        kopiuj(pom_2, pom2);
        iloraz(pom_2, 2 * k + 1);
        if (k % 2 == 0)
            roznica(liczba1, pom_2);
        else
            suma(liczba1, pom_2);

        k++;

        wypiszLiczbe(liczba1, il_m, liczbaMiejsc(pom1));
        il_m = liczbaMiejsc(pom1);
    } while (!czyKoniec(pom1));

     printf ("%c %c\n", 8, 8);
}


int main(int argc, char *argv[])
{
    if (argc <= 1) {
        fprintf(stderr, "Wyznaczanie liczby Pi z nieskonczonego szeregu"
                        " postaci:\n"
                        " pi/4 = 4*arctan(1/5) - arctan(1/239)\n\n"
                        "Sebastian Pawlak\n"
                        "\nBrak parametru!\n./liczba.exe [dokladnosc]\n");
        return -1;
    }

    nCyfr = atol(argv[1]);
    liczba1 = (char *)malloc(nCyfr);
    pom1 = (char *)malloc(nCyfr);
    pom2 = (char *)malloc(nCyfr);
    pom_1 = (char *)malloc(nCyfr);
    pom_2 = (char *)malloc(nCyfr);

    if ((!liczba1) || (!pom1) || (!pom2) || (!pom_1) || (!pom_2)) {
        fprintf(stderr, "Malo pamieci!\n");
        return -1;
    }

    obliczenia();

    return 0;
}
w3cw3c
automatyka przemysłowa