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





