Végtelen sor összegének számítása

Ha hallgattál már zenét vagy néztél filmet online, esetleg próbáltál kiszámolni egy hiteltörlesztést, akkor már találkoztál a végtelen sorokkal – csak épp nem biztos, hogy tudtál róla. Minden digitális hang és kép szinuszjelek végtelen összegeként írható le. A kamatos kamat képlete mögött is egy végtelen geometriai sor rejlik. És bizonyos irracionális számok értékének (pl. π, e) közelítése is lehetséges végtelen sorokkal. E kiragadott példákon felül még számos más alkalmazási területe van a végtelen numerikus soroknak.

A számítógép viszont nem tud „végtelenig” számolni, ezért a végtelent kezelhetővé kell tenni. A következőkben megnézzük, hogyan lehet egy végtelen numerikus sor összegét programban kiszámítani úgy, hogy „elég közel” legyünk az egzakt értékhez.

Ahhoz, hogy egy olyan programot tudjunk írni, ami a sorösszeget jól közelíti, néhány fogalmat és tételt ismerni kell. (Ha valaki felsőbb szintű matematikát tanult, annak mindez nem lesz újdonság).

A sorozatokkal és sorokkal kapcsolatban talán a legfontosabb fogalom a konvergencia. A precíz matematikai definíciónál kicsit lazábban fogalmazva egy végtelen sorozat konvergens, ha az értékei egy bizonyos számértéket (a határértéket) egyre jobban, azaz tetszőlegesen megadott pontossággal megközelít. E korlátnak általában a görög ε (epsilon) a jele. Tehát van olyan index érték, amelynél nagyobb indexű sorozatelemek eltérése az adott ε pontosságnál nem nagyobb. A nem konvergens sorozatokat divergens sorozatoknak nevezik.

Egy végtelen sor akkor konvergens, ha a tagok részösszegeinek sorozata konvergens.

A végtelen sorokat a matematikában a tagok előjele alapján szokták csoportosítani, mert az egyes csoportokra eltérő (lazább vagy szigorúbb) feltételek határozhatók meg a konvergenciára vonatkozóan. Ezekből az egyik a váltakozó előjelű (alternáló) sorok csoportja. Ezek olyan számsorok, amelyekben az egymást követő tagok előjele ellentétes.

Miért érdekesek az alternáló sorok? Azért, mert ha teljesítik még azt a követelményt is, hogy a sor tagjainak abszolút értéke monoton csökken, és a tagok sorozata nullához tart, akkor a sor biztos, hogy konvergens. Az ilyen sorozatokat Leibniz-típusú soroknak (vagy rövidebben Leibniz soroknak) nevezik a konvergencia kritériumot elsőként megfogalmazó Gottfried Wilhelm Leibniz matematikusról. Ilyen sorokra mutat példát a következő ábra. Ezeknél a sorösszeg egzakt formában kifejezhető.

Az alábbi ábrán olyan sorok láthatók, amelyek bár konvergensek és az összeg egzakt formában kifejezhető, de nem Leibniz sorok.

Mindezeket tudva, most már nekiállhatunk a sorösszegszámítás programmal történő megvalósításához. Ez két lépésből fog állni.

Először megpróbáljuk az adott sort Leibniz sorként kezelni. Ez azt jelenti, hogy vesszük a tagok sorozatát és megnézzük egyrészt, hogy a tagok váltakozó előjelűek-e, másrészt, hogy monoton csökkenők-e. Ezeket könnyen, két szomszédos tag vizsgálatával eldönthetjük. Ha ezek teljesülnek, akkor azt kell még megállapítani, hogy a tagok sorozata a nullához tart. Ez sem nehéz, hiszen meg tudjuk mondani, hogy mikor tartunk annál a tagnál, amely már a meghatározott epsilon konvergencia-hibahatáron belül van. Azonban az esetleges kerekítésből adódó hibák kiküszöbölésére az epsilon korláton belülre kerülést adott számú tagra fogjuk vizsgálni, és csak akkor adjuk vissza a sorösszeget, ha minden vizsgált tag már biztosan benne van a toleranciasávban.

A második lépésre akkor kerül sor, ha menet közben kiderül, hogy nem Leibniz-típusú a sor. Ebben az esetben a sorösszegzést egy megadható számú tagra fogjuk végezni, és ezt tekintjük a sorösszegnek.

Miért van értelme e két lépésnek? Azért, mert ha Leibniz sorról van szó, akkor tudjuk, hogy biztosan konvergens. Ezért az epsilon értékétől függően tetszőleges pontossággal ki tudjuk számolni a sorösszeget. A számítási igény, vagyis az, hogy hány tagot szükséges az adott pontossághoz figyelembe venni, a konvergencia gyorsaságától fog függni. Gyorsnál kevés tagot, lassúnál többet kell összegezni. Ha túl lassú lenne a konvergencia, akkor erre vonatkozóan is be állíthatunk egy korlátot, amit elérve ezt jelezzük.

Ha viszont kiderül, hogy nem Leibniz sorral van dolgunk, akkor nem lehetünk biztosak még abban sem, hogy egyáltalán konvergens-e a sor. Ezért nem tudunk más tenni, minthogy az igényeinknek megfelelő, előre megadott számú tagra végezzük el az összegzést. Ilyenkor azonban még ha konvergens is a sor, a tényleges és a számolt sorösszeg eltérését nem tudjuk pontosan megmondani. De minél több tagra számolunk, annál jobban közelítünk.

Ezen elvek alapján definiálunk egy SeriesSumCalculator osztályt az alább látható módon. Ebben a series_sum() nyilvános metódus egy paraméter nélküli függvényt fogad argumentumként, ami egy olyan iterálható objektummal tér vissza, amely a sor tagjainak értékét folyamatosan tudja szolgáltatni. Azért nem közvetlenül az iterálható objektumot lehet megadni, mert ha menet közben kiderül, hogy nem Leibniz sorról van szó, akkor az alternatív ágban történő összegzés hibás lenne, hiszen a tagsorozat elemeinek egy része már fel lett használva. Azzal, hogy a leírt tulajdonságú függvényt adjuk át argumentumként, annak meghívásakor a sorozatot generáló objektum újra előáll, és így ismét az első elemtől kezdve lehet kikérni a sor tagjait.

A teszteléshez a következő kódrészletben mutatott módon definiáltunk függvényeket, amelyek különböző sorok tagjainak sorozatát szolgáltató iterátorokkal térnek vissza. Ezek között a fenti ábrán mutatott minden Leibniz sor szerepel, az utána következő ábrán láthatókból pedig három.

A tesztkód és az eredmények alább láthatók.

Megfigyelhetjük, hogy a Leibniz-típusú sorok között vannak, amelyek nagyon gyorsan konvergálnak, és vannak, amelyek meglehetősen lassúak. A nem Leibniz-típusú sorok esetében a konvergencia sebességére nem tudunk következtetni, hanem csak azt tudjuk nézni, hogy a megadott számú tag összegzése esetén mennyire tudtuk megközelíteni az elméleti sorösszeg értékét.

Ebben a bejegyzésben az iterálható objektumok, iterátorok és generátorok, valamint az itertools modul iterátorai domináltak. A fogalmakról egy rövid összefoglalót ad a „Mi az iterátor, generátor, generátor-iterátor és iterátor generátor, generátorfüggvény és -kifejezés?” című korábbi blog bejegyzés. Részleteiben, példákkal magyarázva a Python tudásépítés lépésről lépésre című e-könyvben a „Kérem a következőt! – iterálható objektumok”, a „Kifogyhatatlan sorozatlövők – generátorfüggvények” fejezetekben, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezeten belül a „Speciális iterátorok” című alfejezetben lehet olvasni.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.