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:
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.
wyznaczając kolejne pochodne funkcji y = arctan(x) mamy:
stąd:
zatem:
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:
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; }