Differentiaaliyhtälöryhmän numeerinen ratkaiseminen

Ensimmäisen kertaluvun differentiaaliyhtälöryhmä — vaikkapa korkeamman kertaluvun yhtälöä vastaava normaaliryhmä — voidaan ratkaista numeerisesti täsmälleen samanlaisilla kaavoilla kuin ensimmäisen kertaluvun yhtälö y'  =  f(x, y). Edellytyksenä luonnollisesti on, että alkuehto on annettu.

Seuraavat koodit ovat täsmälleen samat kuin ensimmäisen kertaluvun yhtälöä koskevassa esimerkissä. (Solut on ajettava, jotta määritelmät tulevat voimaan.)

euler[f_, x0_, y0_, h_, xend_] := 
      Module[{X, Y, kend}, 
X[0] = 
          x0; 
X[k_] := X[k] = 
            X[k - 1] + h; 
Y[0] = 
          y0; 
Y[k_] := Y[k] = 
            Y[k - 1] + 
              h f[X[k - 1], Y[k - 1]]; 
kend = 
          Round[(xend - x0)/h]; 
Table[{X[k], 
            Y[k]}, {k, 0, kend}]]

pareuler[f_, x0_, y0_, h_, xend_] := 
      Module[{X, Y, kend}, 
X[0] = 
          x0; 
X[k_] := X[k] = 
            X[k - 1] + h; 
Y[0] = 
          y0; 
Y[k_] := Y[k] = 
            Y[k - 1] + 
              h/2 (f[X[k - 1], Y[k - 1]] + 
                    f[X[k], 
                      Y[k - 1] + 
                        h f[X[k - 1], 
                            Y[k - 
                                1]]]); 
kend = 
          Round[(xend - x0)/h]; 
Table[{X[k], 
            Y[k]}, {k, 0, kend}]]

rungekutta[f_, x0_, y0_, h_, xend_] := 
      Module[{X, Y, kend}, 
X[0] = 
          x0; 
X[k_] := X[k] = 
            X[k - 1] + h; 
Y[0] = 
          y0; 
Y[k_] := Y[k] = 
            Y[k - 1] + 
              1/6 Module[{k1, k2, k3, k4}, 
                  k1 = h f[X[k - 1], 
                        Y[k - 1]]; 
k2 = 
                    h f[X[k - 1] + h/2, 
                        Y[k - 1] + k1/2]; 
k3 = 
                    h f[X[k - 1] + h/2, 
                        Y[k - 1] + k2/2]; 
k4 = 
                    h f[X[k - 1] + h, 
                        Y[k - 1] + k3]; 
k1 + 
                    2 k2 + 2 k3 + k4]; 
kend = 
          Round[(xend - x0)/h]; 
Table[{X[k], 
            Y[k]}, {k, 0, kend}]]

adamsbashforth[f_, x0_, y0_, h_, xend_] := 
      Module[{X, Y, kend}, 
X[0] = 
          x0; 
X[k_] := X[k] = 
            X[k - 1] + h; 
Y[0] = 
          y0; 
Y[k_ /; k <= 3] := Y[k] = 
            Y[k - 1] + 
              1/6 Module[{k1, k2, k3, k4}, 
                  k1 = h f[X[k - 1], 
                        Y[k - 1]]; 
k2 = 
                    h f[X[k - 1] + h/2, 
                        Y[k - 1] + k1/2]; 
k3 = 
                    h f[X[k - 1] + h/2, 
                        Y[k - 1] + k2/2]; 
k4 = 
                    h f[X[k - 1] + h, 
                        Y[k - 1] + k3]; 
k1 + 
                    2 k2 + 2 k3 + k4]; 
Y[
            k_ /; k > 3] := Y[k] = 
            Y[k - 1] + 
              h/24 (55 f[X[k - 1], Y[k - 1]] - 
                    59 f[X[k - 2], Y[k - 2]] + 
                    37 f[X[k - 3], Y[k - 3]] - 
                    9 f[X[k - 4], 
                        Y[k - 4]]); 
kend = 
          Round[(xend - x0)/h]; 
Table[{X[k], 
            Y[k]}, {k, 0, kend}]]

Esimerkkinä olkoon differentiaaliyhtälö y''  +  x y  =  0 alkuehtona y(0)  =  0, y' (0)  =  1. Kyseessä on Airyn yhtälö, jossa x-akselin suunta on käännetty, ts. x:n merkki on vaihdettu. Yhtälöä vastaava normaaliryhmä muodostuu kahdesta yhtälöstä y'  =  z, z'  =  - x y alkuehtona y(0)  =  0, z(0)  =  1. Vektorimuodossa normaaliryhmä on Y'  =  F(x, Y); oikean puolen vektoriarvoinen funktio määritellään Mathematicalle seuraavasti:

f[x_, {y_, z_}] := {z,  - x y}

Askelpituus ja arvo, jolla alkuehto annetaan:

h = 0.1

x0 =  0

Tuntemattomana funktiona on vektori Y  =  (y, z), joten alkuarvo on myös vektori:

y0  =  {0, 1}

Lasketaan välillä [0, 10]:

xend =  10

Ratkaisufunktion approksimaatio jokaisessa pisteessä (Subscript[x, k]) saadaan kaksikomponenttisena vektorina. Edellinen komponentti on itse funktion y arvo, jälkimmäinen sen derivaatan z  =  y' arvo:

eulerdata =  
        euler[f, x0, y0, h, xend];
Short[eulerdata, 10]

Muuttujaan eulerdata talletettu tulostus on huomattavan pitkä, ja siitä on Mathematican Short-funktion avulla kirjoitettu näkyviin vain osa. Merkintä <<...>> osoittaa, kuinka monta termiä on jätetty pois.

Tästä voidaan poimia vain argumentin x ja funktion y arvot sijoitussäännöllä, minkä jälkeen voidaan piirtää kuva:

poiminta =  {x_, {y_, z_}} :> {x, y}

eulerdata =  
        eulerdata /. poiminta;
Short[eulerdata, 10]

eulerkuva =  ListPlot[eulerdata]

Vastaavat laskut muita menetelmiä käyttäen:

pareulerdata =  
        pareuler[f, x0, y0, h, xend] /. 
          poiminta;
Short[pareulerdata, 10]

pareulerkuva =  ListPlot[pareulerdata]

rungekuttadata =  
        rungekutta[f, x0, y0, h, xend] /. 
          poiminta;
Short[rungekuttadata, 10]

rungekuttakuva =  ListPlot[rungekuttadata]

adamsbashforthdata =  
        adamsbashforth[f, x0, y0, h, xend] /. 
          poiminta;
Short[adamsbashforthdata, 10]

adamsbashforthkuva =  
      ListPlot[adamsbashforthdata]

Esimerkkinä oleva alkuarvoprobleema voidaan ratkaista myös tarkasti Airyn funktioiden avulla. Mathematica (versio 4.1) ei kuitenkaan osaa ratkaista kyseessä olevaa normaaliryhmää, joten on tarkasteltava alkuperäistä Airyn yhtälöä:

tarkkaratkaisu =  
      y /. First[
          DSolve[{y''[x]  +  x y[x] == 0, 
              y[0] == 0, y'[0] == 1}, y, 
            x]]

tarkkakuva =  
      Plot[tarkkaratkaisu[x], {x, 0, xend}]

Eri menetelmillä saadut approksimaatiot ja tarkka ratkaisu verrattuina:

kuva =  
      Show[eulerkuva, pareulerkuva, rungekuttakuva, 
        adamsbashforthkuva, tarkkakuva, 
        PlotRange -> All]

Tarkan ratkaisun tarkkuus riippuu luonnollisesti siitä, miten Mathematica laskee ratkaisussa esiintyvien Airyn funktioiden ja gammafunktion arvot.

Vertailun vuoksi alkuarvoprobleema voidaan ratkaista myös Mathematican käyttämällä numeerisen ratkaisemisen algoritmilla, joka on edellä esitettyjä kehittyneempi:

numeerinenratkaisu = 
      y /. First[
          NDSolve[{y''[x]  +  x y[x] == 0, 
              y[0] == 0, y'[0] == 1}, 
            y, {x, x0, xend}]]

Tulos on interpolaatiofunktio, jonka arvo välin päätepisteessä voidaan laskea tavalliseen tapaan. Eri ratkaisujen antamat arvot loppupisteessä x  =  10:

{Last[eulerdata][[2]], Last[
          pareulerdata][[2]], Last[
          rungekuttadata][[2]], Last[
          adamsbashforthdata][[2]], tarkkaratkaisu[xend] // N, 
      numeerinenratkaisu[xend]}

SKK 30.04.2001