Differenciálegyenletek numerikus megoldása 2010, Pécsi Tudományegyetem Kollár Bálint (Utolsó változtatás: 2010. október 23.) Közönséges differenciálegyenleten olyan egyenletet értünk, amelyben a meghatározandó ismeretlen egy egyváltozós függvény, és az egyenletben ezen ismeretlen függvény különböző rendű deriváltjai szerepelnek, illetve egy- és többváltozós ismert (adott) függvények. A differenciálegyenlet rendje a benne szereplő ismeretlen függvény legmagasabb rendű deriváltjának a rendje. A differenciálegyenlet megoldásához kezdeti feltételekre van szükség, illetve másod- vagy magasabb rendű differenciálegyenleteknél peremfeltételeket is megadhatunk a kezdeti feltételek helyett. A differenciálegyenletek vagy differenciálegyenlet-rendszerek numerikus megoldását az a puszta tény motiválja, hogy a természetben előforduló valós problémákat leíró differenciálegyenletek (egyenletrendszerek) túlnyomó többsége analitikus formában nem megoldható, nincs a megoldásnak ismert zárt alakja. (Példának okáért gondolhatunk kaotikus rendszerekre.) A továbbiakban a differenciálegyenletek numerikus megoldásának néhány közkedvelt módját ismertetem. 1. Euler-módszer Az Euler-módszer a legegyszerűbb módszer differenciálegyenlet-rendszerek numerikus megoldására. A következő kezdeti feltételekkel adott differenciálegyenletet szeretnénk megoldani: dy(t) dt = f(t, y(t)), (1) y(t0) = y0. (2) (Azaz kíváncsiak vagyunk y(t ) értékére minden számunkra érdekes T időpillanatban úgy, hogy közben y(t ) kielégítse y(t = t0) = y0, azaz a (2) feltételt.) Megoldás: (1) mindkét oldalát integráljuk: T majd a Newton-Leibniz szabályt alkalmazzuk: ami átrendezés és (2) felhasználása után: t0 dy(t) T dt = f(t, y(t))dt, (3) dt t0 y(t ) y(t0) = y(t ) = y0 + T T t0 t0 f(t, y(t))dt, (4) f(t, y(t))dt. (5) Diszkretizáljuk az utolsó kifejezés jobb oldalán álló integrált (bontsuk véges t összegekre a t0-tól T -ig terjedő intevallumot)! T y(t ) = y0 + f(t, y(t)) t. (6) t=t0 1
Ne felejtsük el, hogy ez csak t 0 határesetben megy át (5) be. Elsőrendű Euler-módszer: Használjuk (6) kifejezést véges t értékek mellett. Mit is jelent ez a gyakorlatban? y(t) ismeretlen függvény kezdeti értékét ismerjük (2), (1) kifejezésbe írva a kezdeti értéket megkapjuk az ismeretlen függvény deriváltját ebben a kezdeti időpillanatban. A jobb oldali szumma tartalmaz ismeretlen tagokat is (hiszen függ y(t) olyan értékeitől amiket még nem ismerünk), de a szumma legelső tagja kiszámolható, ami a következő kifejezéshez vezet: y(t0 + t) = y0 + f(t0, y(t0)) t. (7) (Ha egy pillanatra visszaírjuk f(t, y(t)) helyére y(t) deriváltját (1)-ből, akkor a következőt kapjuk: y(t0 + t) = y0 + dy(t) dt t. (8) t=t0 Vegyük észre, hogy ez nem más mint egy Taylor-sorfejtés az első tagig! Ennek később még hasznát fogjuk venni.) Térjünk vissza (7) kifejezéshez. Ez a kifejezés már csupa ismert dolgot tartalmaz, tehát építőkőként használhatjuk és iteratív módon algoritmust írhatunk belőle, mely megoldja a kívánt feladatot! Minden egyes iterácíós lépésben pusztán az aktuális időpillanatban ismert függvény és derivált értéket kell felhasználnunk (az ilyen módszereket nevezzük egylépésesnek). Tehát az Euler-módszer egy tetszőleges lépése így írható: y(t + t) = y(t) + f(t, y(t)) t. (9) Az algoritmizálásban kevésbé jártasak kedvéért álljon itt egy lehetséges pszeudo-kód, mely megvalósítja az elsőrendű Euler-módszert. Ismert paraméterek: delta_t: egy időlépés hossza f(t,y): y(t) ismeretlen függvény deriváltja t időpillanatban t0: kezdeti időpillanat (gyakorlatban célszerű 0-át választani) y0: kezdeti érték a kezdeti időpillanatban T: végső időpillanat Inicializálás: y := y0 t := t0 Eljárás: Ciklus amíg t < T y := y + f(t,y) * delta_t t := t + delta_t 2
2. Newton-féle mozgásegyenlet megoldása Feladat: adott a következő Newton-féle mozgásegyenlet ẍ(t) = 1 F (x, ẋ, t) m (10) x(t0) = x0 (11) ẋ(t0) = v0, (12) ahol a " " (pont) a fizikusok között elterjedt jelölés az idő szerinti deriválás rövid jelzésére. Oldjuk meg a feladatot elsőrendű Euler-módszer segítségével! Megoldás: Vegyük észre, hogy a Newton-féle mozgásegyenlet másodrendű. Az Euler-módszer mégis alkalmazható rá. Vezessünk be egy új változót (a sebességet) és bontsuk szét a differenciálegyenletet két elsőrendű csatolt differenciálegyenletre! v(t) = ẋ(t) (13) v(t) = 1 F (x, v, t) m (14) x(t0) = x0 (15) v(t0) = v0. (16) Majd a (9) kifejezés alapján lépésenként iteráljuk az egyenleteket, tehát x(t + t) = x(t) + v(t) t (17) v(t + t) = v(t) + 1 F (x, v, t) t, m (18) (19) természetesen az iteráció első lépéseben a (15) és (16) kezdeti feltételeket kell felhasználni. 3
Pszeudo-kód a Newton-féle mozgásegyenlet megoldásához elsőrendű Euler-módszerrel: Ismert paraméterek: delta_t: egy időlépés hossza F(x,v,t): erőtörvény m: tömeg t0: kezdeti időpillanat (gyakorlatban célszerű 0-át választani) x0: kezdeti hely v0: kezdeti sebesség T: végső időpillanat Inicializálás: x := x0 v := v0 t := t0 Eljárás: Ciklus amíg t < T x_uj := x + v * delta_t v_uj := v + (1/m) * F(x,v,t) * delta_t v := v_uj x := x_uj t := t + delta_t Figyeljük meg, hogy a változók csak akkor kapják meg új értéküket, mikor már minden derivált kiértékelése megtörtént az előző időpillanatban érvényes értékekkel! Ez az Euler-módszer levezetéséből következő szabály, ha nem így csinálnánk az algoritmust, akkor az helytelen eredményt adna, illetve instabil lenne. 4
3. Peremérték feladatok megoldása A Newton-féle mozgásegyenlet megoldása már valós probléma, ráadásul másodrendű, tehát a kezdeti felételek helyett peremfeltételek is megadhatóak. E feladatok igen fontosak reális rendszereknél, rengeteg valós kérdést fel lehet tenni. (Pl.: Merre célozzunk egy ágyúval, hogy eltaláljuk vele az ellenség lőporraktárát.) Ezek a feladatok általában sokkal nehezebben oldhatóak meg és sokkal számításigényesebbek, mint a kezdetiérték feladatok. Egyáltalán azt is meg kell vizsgálni, hogy a feladatnak egyáltalán van-e megoldása adott peremfeltételek mellett. (Ha az ágyúnk 100 km-re van a céltól és csak pár grammnyi lőporunk van, akkor bajos megtalálni a megoldást - eltalálni a célt.) Nézzük meg a konkrét ágyús példánkant! Adott: ẍ(t) = 0 (20) ÿ(t) = g m (21) x(0) = 0 (22) y(0) = 0 (23) (ẋ(t0)) 2 + (ẏ(t0)) 2 = v0 (24) x(t) = c (25) y(t) = 0, (26) azaz a (0; 0) pontban állunk, az ágyúgolyó kezdősebessége v0, és a célpont a (c; 0) pontban tartózkodik, közegellenállas nincs, csak a gravitáció hat a golyóra. (Az egyszerűség kedvéért feltesszük hogy c pozitív.) Az előbbieket követve könnyen felírhatjuk az Euler-módszerrel való iteráció menetét, a kezdősebesség irányát azonban nem tudjuk - hiszen ennek a meghatározása lenne a feladatunk. Tudjuk, hogy az ágyúgolyónk legmesszebbre akkor repül, ha 45 fokban lőjük ki (a cél felé). Tehát indítsuk el az algoritmust a 45 foknak megfelelő kezdősebességből. Ha a golyónk nem éri el a célt - annál hamarabb leesik, akkor tudjuk, hogy a feladatnak nincs megoldása, az adott kezdősebesség mellett a golyót nem lehet eljuttatni a célig. Ha pont eltalálja (itt tegyük fel, hogy az eltalálás azt jelenti, hogy adott hibahatárnál - a cél méreténél - közelebb esik le), akkor szerencsénk van, végeztünk. Viszont valószínűbb, hogy messze túllőttünk a célon. Ilyenkor indul az igazi megoldás, keressük felező kereséssel meg az ideális szöget! Vegyünk fel két intervallum határt, kezdetben ez 0 és 45. Lőjük ki a golyót a két határ között középen elhelyezkedő számnak megfelelő szöggel (22,5 fok). Ha túllöttünk a célon, akkor a felső határt csökkentsük le az előző lövés szintjére, ha túl közel lövünk, akkor viszont az alsó határt emeljük az előző lövés szintjére. Majd újra a két határ közt félúton (átlag) lévő szöggel próbálkozzunk. Ismételjük ezt az eljárást addig, amíg hibahatáron belül el nem találjuk a célt! (Itt vegyük észre, hogy ennek az ágyúgolyós feladatnak két megoldása is lehet, tehát ha 45 foknál nagyobb szögek között keresünk, akkor is találhatunk olyan irányt, amikor a golyó eltalálja a célt - ez esetben egy nagyobb íven repülve.) 5
Az algoritmus pszeudo-kódja a következő: Ismert paraméterek: delta_t: egy időlépés hossza g: gravitációs állandó m: tömeg c: célpont helye v0: kezdeti sebesség err: célpont helyének hibája Eljárás: vx := cos(45) * v0; vy := sin(45) * v0; x := 0 y := 0 t := 0 Ciklus amíg y >= 0 x_uj := x + vx * delta_t y_uj := y + vy * delta_t vx_uj := vx vy_uj := vy - (g/m) * delta_t x := x_uj y := y_uj vx := vx_uj vy := vy_uj t := t + delta_t Ha x-c < err akkor kiírat(45 fok), algoritmus vége Ha x < c akkor kiírat(a cél túl messze van), algoritmus vége fok_also := 0 fok_felso := 45 Ciklus amíg x-c > err vx := cos((fok_also + fok_felso) / 2) * v0; vy := sin((fok_also + fok_felso) / 2) * v0; x := 0 y := 0 t := 0 Ciklus amíg y >= 0 x_uj := x + vx * delta_t y_uj := y + vy * delta_t vx_uj := vx vy_uj := vy - (g/m) * delta_t x := x_uj y := y_uj vx := vx_uj vy := vy_uj 6
t := t + delta_t Ha x-c > err akkor Ha x > c akkor fok_felso := (fok_also + fok_felso) / 2 Ha c > x akkor fok_also := (fok_also + fok_felso) / 2 kiírat((fok_also + fok_felso) / 2 fok) Tudni kell azonban, hogy nem minden peremérték feladatot ilyen egyszerű megoldani. Kaotikus rendszereknél ez a módszer nem működik, ott általában nincs más megoldás, mint puszta erővel (brute force) a lehető legtöbb kezdőállapotot végignézni és megtalálni azt, ami nekünk megfelő. 4. Euler-módszer hibája Fejtsük Taylor-sorba az ismeretlen y(t) függvény megváltozását kis t idő alatt: y(t + t) = y(t) + dy(t ) dt t + 1 t 2 =t d 2 y(t ) dt 2 ( t) 2 +.... (27) t =t Vessük ezt össze a (9) kifejezéssel, azaz az elsőrendű Euler-módszer egy időlépésével. A Taylor-sorfejtés jól viselkedő függvények esetén egzakt, tehát becsülhetjük vele az Euler-módszer hibáját. Ha a két kifejezés különbségét vesszük, akkor minden kiesik, kivéve a másod- és magasabb rendű tagokat, tehát ebből láthatjuk, hogy az elsőrendű Euler-módszer lépésenként egy másodrendű hibát ejt! Az összlépésszám hibája tehát e másodrendű hibák összegével arányos, ami kellően nagyra tud nőni akár egyszerű rendszerek esetén is - a függvények tipikusan "felrobbannak" a rendszer összenergiája exponenciálisan elszáll tehát a gyakorlatban az elsőrendű Euler-módszert állatorvosi ló szerepén kívül másra nem használják. A továbbiakban az elsőrendű Euler-módszernél hatékonyabb módszereket mutatok be, melyek többnyire mentesek az ilyen veszélyektől, komolyabb számításokhoz is használhatóak. 5. Magasabb rendű Euler-módszerek Mint korábban láttuk, az elsőrendű Euler-módszer felfogható úgy, hogy egy ismeretlen y(t) függvény kis megváltozását a Taylor-sorfejtés első rendjéig közelítjük. Ha azonban több tagot is felhasználunk, akkor pontosabb lesz a közelítés, így jutunk el a magasabb rendű Euler-módszerekig. Ezek alapján könnyű belátni, hogy egy másodrendű Euler-módszer egy lépése így néz ki a harmadrendűé pedig y(t + t) = y(t) + f(t, y(t)) t + d f(t, y(t)) ( t) 2, (28) dt 2! y(t + t) = y(t) + f(t, y(t)) t + d f(t, y(t)) ( t) 2 + d2 f(t, y(t)) ( t) 3, (29) dt 2! dt 2 3! és így tovább. Felmerülhet a kérdés, hogy ha ez ilyen egyszerű, akkor miért nem állunk meg itt és használunk sokadrendű Euler-módszereket? A válasz abban rejlik, hogy f(t, y(t)) (azaz y(t) időderiváltjának) a deriváltjait is ki kell számítani, ezek a deriváltak pedig nagyon bonyolultak lehetnek (vagy nem is 7
tudjuk az erőtörvényt deriválni, mert nem ismerjük a zárt alakját - pl. csak mérési adataink vannak). A behelyettesítés rengeteg gépidőt elvehet, illetve a bonyolult kifejezések kiértékelése során rengeteg numerikus (kerekítési) hibát ejthetünk. Extrém esetben a Taylor-sorfejtés nem konvergál megfelelően, épp hogy romlik tőle a közelítés. A másik lehetőségünk hogy t-t minden határon túl csökkentjük. Ez egyrészt szélsőséges módon megnöveli a számításigényt, másrészt ha túl picire választjuk meg, akkor kerekítési gondok is felléphetnek: a számítógép véges pontossággal számol, ha egy túl nagy számhoz adunk egy túl picit (a változás egy pici t szorzó!) akkor értékes tizedesjegyeket veszthetünk, pont az áhított pontosságot rontva. Tehát láthatjuk, hogy más módszert kell találnunk, ha a pontosságot szeretnénk a sebességgel és a könnyű kiszámíthatósággal ötvözni. 6. Középpontos módszer (Midpoint method) A középpontos módszer a nevét onnan kapta, hogy egy adott időpillanatban nem az ott érvényes deriválttal lépünk, hanem a kívánt lépésköz felénél érvényes deriválttal: a keresett görbe meredekségét a lépés két végpontja között félútról válasszuk. Tehát: y(t + t) = y(t) + f(t + t t, y(t + )) t. (30) 2 2 A gond csupán az, hogy nem ismerjük f függvény értékét t + t/2 időpillanatban, csak t időpillanatban. Viszont közelíthetjük az értéket az elsőrendű Euler-módszerrel! Így a végeredmény: y(t + t) = y(t) + f(t + t 2 Hibaszámítás: Az előző kifejezést írjuk át Taylor-sorhoz hasonlító egyeszerűbb alakba Fejtsük sorba első rendig y(t + t 2 ) -t majd bontsuk ki a kifejezést t d y(t + y(t + t) = y(t) + dt y(t + t) = y(t) + d dt (, y(t) + f(t, y(t)) t) t. (31) 2 2 ) y(t) + d y(t) dt t. (32) ) t t, (33) 2 y(t + t) = y(t) + d y(t) t + d2 y(t) ( t) 2. (34) dt dt 2 2 Összevetve a kifejezést az ismeretlen függvény Taylor sorfejtésével láthatjuk, hogy másodrendig egzakt a középpontos módszer, a hibája harmadrendű. A középpontos módszer előnyei: több számítással jár mint az elsőrendű Euler, viszont másodrendű. A másodrendű Eulerhez képest azt nyerjük, hogy nem kell az ismeretlen függvény második deriváltját kiszámítani, elég az első deriváltat használni, így összességében elmondhatjuk hogy általában kevesebb számítást igényel. A módszer gyakorlatban már használható, ám komoly számításhoz még nem elegendő, viszont ahol a pontosság nem fontos, ott többnyire elfogadható stabilitással működik. 8
Pszeudo-kód a középpontos módszerhez: Ismert paraméterek: delta_t: egy időlépés hossza f(t,y): y(t) ismeretlen függvény deriváltja t időpillanatban t0: kezdeti időpillanat (gyakorlatban célszerű 0-át választani) y0: kezdeti érték a kezdeti időpillanatban T: végső időpillanat Inicializálás: y := y0 t := t0 Eljárás: Ciklus amíg t < T k := y + f(t,y) * delta_t / 2 y := y + f(t + delta_t / 2, k) * delta_t t := t + delta_t 7. Runge-Kutta módszer A középpontos módszernél láthattuk az alapötletet: számoljunk kicsit többet, de csak az első deriváltat felhasználva, és ha a részeredményeket megfelelően súlyozva felhasznájuk, akkor ezzel kisebb hibát véthetünk, kevesebbet számolva. (Hivalatosan a középpontos módszer is a Runge-Kutta módszerek közé tartozik.) A legelterjedtebb és igen gyakran használt a negyedrendű Runge-Kutta módszer. Az alábbi segédderiváltakat kell hozzá kiszámolni (ebben a sorrendben): Ezekből számoljuk azt a meredekséget, mellyel végül lépünk: k 1 = f(t, y(t)) (35) k 2 = f(t + t t, y(t) + 2 2 k 1) (36) k 3 = f(t + t t, y(t) + 2 2 k 2) (37) k 4 = f(t + t, y(t) + tk 3 ). (38) y(t + t) = y(t) + 1 6 (k 1 + 2k 2 + 2k 3 + k 4 ) t. (39) A módszer (mint ahogy a neve is mutatja) negyedrendig egzakt, tehát lépésenként csak egy ötödrendű hibát ejt. (A hibaszámítást a vállakozó kedvű olvasókra bízom.) Gyakorlatban ez a módszer stabil, megfelelően használva komoly, tudományosan elvárt pontosságú számítások is végezhetőek vele. Ennél magasabb rendű módszereket csak különösen fontos vagy szélsőségesen magas pontosságot igénylő helyeken használnak (űrkutatás, nemzetvédelem). 9
Pszeudo-kód a negyedrendű Runge-Kutta módszerhez Ismert paraméterek: delta_t: egy időlépés hossza f(t,y): y(t) ismeretlen függvény deriváltja t időpillanatban t0: kezdeti időpillanat (gyakorlatban célszerű 0-át választani) y0: kezdeti érték a kezdeti időpillanatban T: végső időpillanat Inicializálás: y := y0 t := t0 Eljárás: Ciklus amíg t < T k1 := f(t,y) k2 := f(t + delta_t / 2, y(t) + delta_t / 2 * k1) k3 := f(t + delta_t / 2, y(t) + delta_t / 2 * k2) k4 := f(t + delta_t, y(t) + delta_t * k3) y := y + (k1 + 2 * k2 + 2 * k3 + k4) * delta_t / 6 t := t + delta_t 8. Adaptív lépéshosszváltoztatás Az Euler-módszer bevezetésénél egy integrált (5) diszkretizáltunk (6), de nem tettünk kikötést t lehetséges értékeire, pusztán feltettük, hogy kicsi. A pszeudo-kódokban is látható, hogy a legegyszerűbb megoldás az, ha apró, egyforma t intervallumokra bontjuk az integrálási időt. Ez egy természetes, intuitív feltevés, de kis gondolkodás után finomíthatjuk. A hibászámításoknál látható, hogy a hiba másodrendű, de t-ben és az ismeretlen függvény deriváltjában is másodrendű - a kettő szorzata adja a hiba nagy részét. Ha picit tovább gondolkodunk, ráérezhetünk hogy ez azt jelenti, hogy iteráció közben - tehát az algoritmus futása alatt - új t-ket választhatunk, feltéve hogy az ismeretlen függvény deriváltja (f(t, y(t))) kellően lassan változik! Példa: harmonikus rezgőmozgásnál a nyugalmi helyzete közelében a derivált keveset változik, hiszen a szinuszfüggvény lineárisan indul - a nyugalmi hely környezetében nagy t időkkel léphetünk. Ellenben a maximális amplitúdó közelében (a szinusz csúcsánál) a derivált gyorsan változik, itt apró t időket választhatunk - megtartva a kívánt pontosságot! Összességében elmondható, hogy az adaptív lépéshosszváltoztatás rengeteget gyorsíthat a numerikus rutinokon, miközben egy előre megadott pontossághatárt megtarthatunk. Hogy válasszuk ki az optimális új t lépéshosszt? Használjunk egyszerre egy alacsonyabb és egy magasabb rendű módszert! (Vagy ugyanazt a módszert két különböző t lépésközzel.) A hibát a kettő módszer eredményének különbségéből máris megkaphatjuk. Hasonlítsuk össze ezt a kívánt hibával, ha túl nagy a hibánk, akkor rövidítsük a lépésközt és kezdjük előlről a lépés számítását (azaz ne tegyük még meg a lépést). Ha túl kicsi a hibánk, akkor lépjünk a pontosabb módszerrel, de növeljük meg a lépésközt, ezzel feltéve (és bízva abban), hogy a derivált nem változik a következő lépésig túl sokat, tehát az új, nagyobb lépésköz a következő lépésben az elvárt hibán belül lesz! 10
Felhasznált irodalom Bronstejn, Musiol, Mühlig, Szemengyajev, Matematikai Kézikönyv, 8. kiadás (2004) Saját egyetemi jegyzet 11