Nagypontosságú számolások

Sokan nem is tudják, hogy milyen nagyszerû eszközöket rejt a Unix szerszámosláda, így aztán nem csoda, hogy nem is használják ezeket. Most a bc program lehetõségeit mutatjuk be, mely nagyon kapóra jöhet, amikor egy feladat különleges pontossági követelményeinek a szokványos kalkulátorok 8-10 jegyes számábrázolási pontossága nem felel meg.

A bc program egy olyan nyelvet kínál, amelyen könnyen megfogalmazhatjuk a kívánt pontosságú számábrázolás mellett végezett matematikai mûveleteket. A végrehajtást interaktív parancsokkal, csõvonalas adatátadással vagy szöveges állományokban tárolt programokkal verérelhetjük. A legalapvetõbb elemek a számok, melyeket skaláris vagy tömb típusú változókban tárolhatunk. A változók azonosítója betûvel kezdõdjön (a POSIX szabvány csak egykarakteres neveket engedélyez, de a Linux rendszerekben található bc program hosszabb nevek használatát is megengedi).

Négy beépített változóval érdemes megismerkedni: a scale nevû változó értéke szabja meg, hogy hány tizedes pontossággal történjen a mûveletek végzése, ibase és obase a be- és kimeneti számrendszer konverziókat írja elõ (alaphelyzetben mindkettõ értéke 10, azaz nincs konverzió), last pedig a legutolsó mûvelet eredményét tárolja (ezzel a névvel lehet hivatkozni rá láncmûveletek esetén).

A bc program egy standard matematikai könyvtárral is rendelkezik, az -l parancssori opció megadása esetén automatikusan betölti. Ha a parancssorban állományneveket is megadunk, akkor ezeket sorra értelmezi a program, majd az utolsó állomány feldolgozása után a standard inputról vár újabb parancsokat (kivéve, ha valamelyik állományban quit parncsot helyezünk el, mert ekkor befejezõdik a bc program futása, nem olvas a standard inputról).

A matematikai mûveleteken kívül újabb eljárásokat is definiálhatunk, a programban vezérlõ szerkezeteket is használhatunk. A bc nyelv szintaktikája a C nyelvre emlékeztet, bár annál sokkal egyszerûbb. Az utasítások között megjegyzéseket is elhelyezhetünk, /* és */ jelek közé zárva. Részletes dokumentációt a man bc paranccsal kaphatunk, a programozással kapcsolatban pedig az /usr/doc/bc könyvtárban érdemes szétnézni (sajnos, a CHIP Magazin CD mellékletérõl telepített Red Hat 4.0 rendszer nem tartalmazza ezt az utóbbit).

Ízelítõként csak néhány egyszerû példát mutatunk be:

Pi kiszámítása 250 tizedesjegyre

Indítsuk el a bc programot, töltsük be a matematikai könyvtárat is (az aláhúzott szöveg az, amit a felhasználó gépel be,a többi a gép válasza). Állítsuk be a kívánt pontosságot a scale változóba történõ értékadással, majd adjuk ki a 4*a(1) parancsot. Hosszabb-rövidebb idõ múlva megjelenik az eredmény. Ilyen egyszerû! Kilépni a quit paranccsal lehet. Magyarázat: a Pi=4*arctg(1) azonosságot alkalmazzuk, kihasználva azt, hogy arctg(1) 45 fok, azaz Pi/4, mivel a szöget a matematikai függvények radiánokban mérik.

genius% bc -l
bc 1.03 (Nov 2, 1994)
Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type 'warranty'. 
scale=250
4*a(1)
3.1415926535897932384626433832795028841971693993751058209749445923078\
164062862089986280348253421170679821480865132823066470938446095505822\
317253594081284811174502841027019385211055596446229489549303819644288\
109756659334461284756482337867831652712019088
quit

Faktoriális számítása

Új függvényt definiálunk. A bc program beindítása után ezt írjuk be:

define fact(x) {
       if (x == 1) return(1);
       return(fact(x-1)*x);
               }

Ezek után próbáljuk ki a definiált függvényt, például számítsuk ki 5!, 12! és 50! értékét:

fact(5)
120
fact(12)
479001600
fact(50)
30414093201713378043612608166064768844377641568960512000000000000

Négyzetgyökvonás Newton módszerrel

Csak ujjgyakorlatnak szánjuk az alábbi példát, hiszen a bc programban a négyzetgyök függvény már be van építve (sqrt néven). Newton módszerét követjük: veszünk egy próbaértéket (nevezzük g-nek), majd az eredeti számot osszuk el g-vel (az új mennyiséget jelöljük w-vel). Ha g jó érték lenne, akkor a w hányadossal éppen megegyezne. Ha g még nem elég pontos, akkor a négyzetgyök igazi értéke valahol a (g,w) intervallumban helyezkedik el. Newton iterációs módszerét követve g-t helyettesítjük g és w számtani átlagával, w új értéke pedig a négyzetgyökvonásra megadott szám és g új értékének hányadosa lesz. Az iterációs eljárást addig folytatjuk, amíg a kívánt pontosságot el nem értük. Az alábbi mimtaprogramban az egyszerûség kedvéért elõre meghatározott számú iterációt végzünk, ami nem optimális választás, komoly feladathoz nem ajánlható (pazarolja a gépidõt). Az eljárás listája így néz ki:

define ngy(x) { 
       n=scale/10+20; 
       g=x/2; w=x/g; 
       for (i=1; i<n; i++) { 
           g=(g+w)/2; w=x/g; 
       } 
       return(g); 
}

Próbáljukki az új függvényt! Számítsuk ki 2 négyzetgyökét 250 tizedesjegy pontossággal, majd ellenõrizzük az eredményt négyzetreemeléssel. (Emlékeztetõ: az utoljára kiszámolt eredményre a last változónévvel hivatkozhatunk).

scale=250 
ngy(2) 
1.4142135623730950488016887242096980785696718753769480731766797379907\ 
324784621070388503875343276415727350138462309122970249248360558507372\ 
126441214970999358314132226659275055927557999505011527820605714701095\ 
599716059702745345968620147285174186408891986 
last*last 
1.9999999999999999999999999999999999999999999999999999999999999999999\ 
999999999999999999999999999999999999999999999999999999999999999999999\ 
999999999999999999999999999999999999999999999999999999999999999999999\ 
999999999999999999999999999999999999999999999

Két szám legnagyobb közös osztója

Az alábbi eljárás az Euklideszi algoritmus alapján keresi meg két szám legnagyobb közös osztóját. Euklidesz tétele szerint ha a és b két természetes szám, és a nagyobb mint b (ha véletlenül b a nagyobb, akkor egyszerûen felcseréljük õket...) akkor a és b legnagyobb közös osztója megegyezik a-b és b legnagyobb közös osztójával. A következõ lépésben a-b lesz a új értéke, s most az új a és b közös osztóját keressük. Megint megnézzük, hogy melyik szám a nagyobb, s abból kivonjuk a kisebbiket. Ezeket a kivonogatásokat rekurzív módon addig végezzük, míg az új értekek egyenlõk nem lesznek, s ekkor az új a (illetve az ekkor már vele egyenlõ új b) éppen a legnagyobb közös osztót adja. A program így néz ki:

define lnko (a,b) {
   if (a==b) return(a);
   if (a < b) return (lnko(a,b-a));
   if (b < a) return (lnko(a-b,b));
}

Próbáljuk ki néhány számmal (vigyázat, nincs benne semmiféle ellenõrzés, a felhasználónak kell ügyelnie arra, hogy a bemenõ adatok természetes számok legyenek).

lnko(28,49)
7

lnko(17,5)
1
lnko(123456,234567)
3

lnko(12345678,87654321)
9

Két szám legkisebb közös többszöröse

Egy Homo Primitivicushoz illõ gyalogmódszert alkalmazunk: összeszorozzuk a két számot, s elosztjuk a legnagyobb közös osztóval:

define lkt (a,b) {
      c=a*b
      return(c/lnko(a,b));
}

Nézzünk egy-két mintapéldát is:

lkt(12,15)
60

lkt(128,311)
39808

lkt(12345,54321)
223530915

lkt(123456,234567)
9652901184

lkt(12345678,87654321)
120239113597182


URL of this page: http://esca.atomki.hu
last modification:
Page maintained by Istvan Cserny   <cserny@atomki.hu>