1.4 Numerical Technique: Euler's Method

September 9, 1998

Example One

Consider the initial value problem dw/dt = (3-w)*(w+1) , w(0) = 0 , for t from 0 to 5 . We'll start with a step size h = Float(5, -1) .

> h:=0.5:          # This is our step-size.
n:=ceil(5.0/h):  # This calculates how many steps

                # of size  h  we need to take, to get to 5.0.

t:=0.0:

w:=0.0:         # The initial condition.

data[0]:=[t,w]:  # We store the initial point so we can

                # plot it later.

for i from 1 to n do    # This is how to make a block of

                       # code get executed n times.

  mk := (3-w)*(w+1):

  w := w + h*mk:

  t := t + h:

  data[i] := [t,w]:  # Store the current point so we can

                     # plot it later.

od:              # This ("do" backwards) marks the end

                # of the "do" block.

t:='t': w:='w':  h:='h':  # Clean up

 

datalist1 := [ seq( data[i], i=0..n ) ];

                # This puts all the data into a list

                # suitable for plotting.

plot( datalist1, 0..5, 0..4, style=POINT, color=red);

                # This plots the (approximation to the)

                # solution, with a horizontal (t) range

                # from 0 to 5, and a vertical (w) range

                # of 0 to 4. The POINT style means each

                # data point is marked with a symbol of

                # some kind. If "style=POINT" were omitted,

                # the data points would be joined with

                # straight line segments (try it!).

datalist1 := [[0, 0], [.5, 1.5], [1.0, 3.375], [1.5, 2.5546875], [2.0, 3.346160889], [2.5, 2.593925431], [3.0, 3.323626291], [3.5, 2.624006721], [4.0, 3.305307806], [4.5, 2.648085766], [5.0, 3.2899924...datalist1 := [[0, 0], [.5, 1.5], [1.0, 3.375], [1.5, 2.5546875], [2.0, 3.346160889], [2.5, 2.593925431], [3.0, 3.323626291], [3.5, 2.624006721], [4.0, 3.305307806], [4.5, 2.648085766], [5.0, 3.2899924...datalist1 := [[0, 0], [.5, 1.5], [1.0, 3.375], [1.5, 2.5546875], [2.0, 3.346160889], [2.5, 2.593925431], [3.0, 3.323626291], [3.5, 2.624006721], [4.0, 3.305307806], [4.5, 2.648085766], [5.0, 3.2899924...

[Plot]

From what we can figure out qualitatively, this seems odd.  Let's look at the direction field.

> with(DEtools):
plot1:=plot( datalist1, 0..5, 0..4,style=point,color=red):

plot2:=dfieldplot(diff(w(t),t)=(3-w)*(w+1), [w(t)],t=0..5,w=0..4,color=blue,arrows=line):

with(plots):

display([plot1,plot2]);

[Plot]

We do better if we decrease the step size.  Let's try h = Float(1, -1) .

> h:=0.1:          # This is our new step-size.
n:=ceil(5.0/h):  

t:=0.0:

w:=0.0:         

data[0]:=[t,w]:  

for i from 1 to n do    

  mk := (3-w)*(w+1):

  w := w + h*mk:

  t := t + h:

  data[i] := [t,w]:  

od:              

t:='t': w:='w':  h:='h':    

datalist1 := [ seq( data[i], i=0..n ) ];

plot1:=plot( datalist1, 0..5, 0..4, color=red):

display([plot1,plot2]);

datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...datalist1 := [[0, 0], [.1, .3], [.2, .651], [.3, 1.0388199], [.4, 1.438669202], [.5, 1.819426135], [.6, 2.152280216], [.7, 2.419505246], [.8, 2.618005732], [.9, 2.756211477], [1.0, 2.847783602], [1.1,...

[Plot]

Of course, we can actually solve this initial value problem.

> simplify(dsolve({diff(w(t),t)=(3-w(t))*(w(t)+1),w(0)=0},w(t)));

w(t) = 3*(exp(4*t)-1)/(3+exp(4*t))

And plot the solution and the numerical approximation on the same plot.

> plot3:=plot(3*(exp(4*t)-1)/(3+exp(4*t)),t=0..5,color=green):
display([plot3,plot2,plot1]);

[Plot]

Example Two

Let us consider the initial value problem dy/dt = sin*y , y(0) = 1 .  Suppose we want to estimate y(3) . We can start with a step size of Float(1, -1) .

> h:=0.5:          
n:=ceil(3.0/h):  

t:=0.0:

y:=1.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := sin(y):

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist1 := [ seq( data[i], i=0..n ) ];

plot1:=plot( datalist1, 0..3, 0..4,style=point, color=red):

display([plot1]);

datalist1 := [[0, 1.0], [.5, 1.420735492], [1.0, 1.915116485], [1.5, 2.385769063], [2.0, 2.728713180], [2.5, 2.929337429], [3.0, 3.034669953]]datalist1 := [[0, 1.0], [.5, 1.420735492], [1.0, 1.915116485], [1.5, 2.385769063], [2.0, 2.728713180], [2.5, 2.929337429], [3.0, 3.034669953]]

[Plot]

Now let's try it with a step size half as big.

> h:=0.25:          
n:=ceil(3.0/h):  

t:=0.0:

y:=1.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := sin(y):

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist2 := [ seq( data[i], i=0..n ) ];

plot2:=plot( datalist2, 0..3, 0..4,style=point, color=blue):

display([plot2,plot1]);

datalist2 := [[0, 1.0], [.25, 1.210367746], [.50, 1.444304186], [.75, 1.692306819], [1.00, 1.940463489], [1.25, 2.173575402], [1.50, 2.379516214], [1.75, 2.552122473], [2.00, 2.691102647], [2.25, 2.79...datalist2 := [[0, 1.0], [.25, 1.210367746], [.50, 1.444304186], [.75, 1.692306819], [1.00, 1.940463489], [1.25, 2.173575402], [1.50, 2.379516214], [1.75, 2.552122473], [2.00, 2.691102647], [2.25, 2.79...datalist2 := [[0, 1.0], [.25, 1.210367746], [.50, 1.444304186], [.75, 1.692306819], [1.00, 1.940463489], [1.25, 2.173575402], [1.50, 2.379516214], [1.75, 2.552122473], [2.00, 2.691102647], [2.25, 2.79...datalist2 := [[0, 1.0], [.25, 1.210367746], [.50, 1.444304186], [.75, 1.692306819], [1.00, 1.940463489], [1.25, 2.173575402], [1.50, 2.379516214], [1.75, 2.552122473], [2.00, 2.691102647], [2.25, 2.79...

[Plot]

> y31:=op(2,op(nops(datalist1),datalist1));
y32:=op(2,op(nops(datalist2),datalist2));

deltay3:=y32-y31;

y3:=y32+deltay3;

y31 := 3.034669953

y32 := 2.995696478

deltay3 := -0.38973475e-1

y3 := 2.956723003

Let's see how this compares with the exact solution.

> dsolve({diff(y(t),t)=sin(y(t)),y(0)=1},y(t));

y(t) = arctan(2*exp(t+ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))/(1+exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))), (-exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))+1)/(1+exp(2*t+2*ln(-1/2*(2+2*sq...y(t) = arctan(2*exp(t+ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))/(1+exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))), (-exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))+1)/(1+exp(2*t+2*ln(-1/2*(2+2*sq...

Okay, so now we substitute in t = 3 into the two "solutions" presented here.

> evalf(subs(t=3.0,arctan(2*exp(t+ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))/(1+exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))),(-exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))+1)/(1+exp(2*t+2*ln(-1/2*(2+2*sqrt(1+tan(1)^2))/tan(1)))))));

-3.087208463-0.2229776750e-10*I

> y3exact:=evalf(subs(t=3,arctan(2*exp(t+ln(-1/2*(2-2*sqrt(1+tan(1)^2))/tan(1)))/(1+exp(2*t+2*ln(-1/2*(2-2*sqrt(1+tan(1)^2))/tan(1)))),(-exp(2*t+2*ln(-1/2*(2-2*sqrt(1+tan(1)^2))/tan(1)))+1)/(1+exp(2*t+2*ln(-1/2*(2-2*sqrt(1+tan(1)^2))/tan(1)))))));

y3exact := 2.959825533

> y3exact-y3;

0.3102530e-2

Notice that y3 actual overshoots the exact value.

Example Three

Here's an example of how the numerical method can break down.
Consider
dy/dt = sin(y)*e^t , y(0) = 5 .

> plot2:=dfieldplot(diff(y(t),t)=sin(y)*exp(t), [y(t)],t=0..5,y=0..6,color=blue,arrows=line):
display(plot2);

[Plot]

Notice that there is an equilibrium solution at y = pi .  Let's try Euler's method with a step size of 0.1.

> h:=0.1:          
n:=ceil(6.0/h):  

t:=0.0:

y:=5.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := sin(y)*exp(t):

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist1 := [ seq( data[i], i=0..n ) ];

plot1:=plot( datalist1, 0..6, 0..6,color=red):

display([plot1]);

datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...datalist1 := [[0, 5.0], [.1, 4.904107573], [.2, 4.795615352], [.3, 4.673897842], [.4, 4.539011944], [.5, 4.392066046], [.6, 4.235580340], [.7, 4.073691625], [.8, 3.912012855], [.9, 3.757017985], [1.0,...

[Plot]

Note the misbehavior starting near t = 5 .  See what happens when the step size decreases.

Example Four

Here's another example of how Euler's method can "fail".
Consider
dy/dt = y^2 , y(0) = 1 .  Suppose we try to find the solution on the interval from t = 0 to t = 1 .

> h:=0.1:          
n:=ceil(1.0/h):  

t:=0.0:

y:=1.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := y^2:

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist1 := [ seq( data[i], i=0..n ) ];

plot1:=plot( datalist1, 0..1, 0..10,color=red):

display([plot1]);

datalist1 := [[0, 1.0], [.1, 1.100], [.2, 1.2210000], [.3, 1.370084100], [.4, 1.557797144], [.5, 1.800470338], [.6, 2.124639682], [.7, 2.576049060], [.8, 3.239651936], [.9, 4.289186403], [1.0, 6.12889...datalist1 := [[0, 1.0], [.1, 1.100], [.2, 1.2210000], [.3, 1.370084100], [.4, 1.557797144], [.5, 1.800470338], [.6, 2.124639682], [.7, 2.576049060], [.8, 3.239651936], [.9, 4.289186403], [1.0, 6.12889...datalist1 := [[0, 1.0], [.1, 1.100], [.2, 1.2210000], [.3, 1.370084100], [.4, 1.557797144], [.5, 1.800470338], [.6, 2.124639682], [.7, 2.576049060], [.8, 3.239651936], [.9, 4.289186403], [1.0, 6.12889...

[Plot]

Now let's try it again with a smaller step size: h = Float(5, -2) .

> h:=0.05:          
n:=ceil(1.0/h):  

t:=0.0:

y:=1.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := y^2:

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist2 := [ seq( data[i], i=0..n ) ];

plot2:=plot( datalist2, 0..1, 0..10,color=blue):

display([plot1,plot2]);

datalist2 := [[0, 1.0], [0.5e-1, 1.0500], [.10, 1.105125000], [.15, 1.166190063], [.20, 1.234190026], [.25, 1.310351277], [.30, 1.396202300], [.35, 1.493671343], [.40, 1.605224047], [.45, 1.734061259]...datalist2 := [[0, 1.0], [0.5e-1, 1.0500], [.10, 1.105125000], [.15, 1.166190063], [.20, 1.234190026], [.25, 1.310351277], [.30, 1.396202300], [.35, 1.493671343], [.40, 1.605224047], [.45, 1.734061259]...datalist2 := [[0, 1.0], [0.5e-1, 1.0500], [.10, 1.105125000], [.15, 1.166190063], [.20, 1.234190026], [.25, 1.310351277], [.30, 1.396202300], [.35, 1.493671343], [.40, 1.605224047], [.45, 1.734061259]...datalist2 := [[0, 1.0], [0.5e-1, 1.0500], [.10, 1.105125000], [.15, 1.166190063], [.20, 1.234190026], [.25, 1.310351277], [.30, 1.396202300], [.35, 1.493671343], [.40, 1.605224047], [.45, 1.734061259]...datalist2 := [[0, 1.0], [0.5e-1, 1.0500], [.10, 1.105125000], [.15, 1.166190063], [.20, 1.234190026], [.25, 1.310351277], [.30, 1.396202300], [.35, 1.493671343], [.40, 1.605224047], [.45, 1.734061259]...

[Plot]

The first gave Float(6128898403, -9) as the value of y(1) , the second Float(9552668001, -9) .  According to our rule of thumb, the true solution ought to be somewhere between Float(6128898403, -9) and

> 9.552668001+(9.552668001-6.128898403);

12.97643760

So let's halve the step size once again.

> h:=0.025:          
n:=ceil(1.0/h):  

t:=0.0:

y:=1.0:         

data[0]:=[t,y]:  

for i from 1 to n do    

  mk := y^2:

  y := y + h*mk:

  t := t + h:

  data[i] := [t,y]:  

od:              

t:='t': y:='y':  h:='h':    

datalist3 := [ seq( data[i], i=0..n ) ];

plot3:=plot( datalist3, 0..1, 0..10,color=green):

display([plot1,plot2,plot3]);

datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...datalist3 := [[0, 1.0], [0.25e-1, 1.02500], [0.50e-1, 1.051265625], [0.75e-1, 1.078894610], [.100, 1.107994949], [.125, 1.138686269], [.150, 1.171101429], [.175, 1.205388393], [.200, 1.241712422], [.2...

[Plot]

What?  Float(1543425786, -8) ?  That's quite a bit out of the range we expect.
What is going on?  Well, let's look at the analytic solution.

> dsolve({diff(y(t),t)=y(t)^2,y(0)=1},y(t));

y(t) = -1/(t-1)

The solution blows up at t = 1 .  We can't expect to get a good numerical approximation to this solution near t = 1 using Euler's method.

> plot4:=plot(-1/(t-1),t=0..1,color=black):
display([plot1,plot2,plot3,plot4]);

[Plot]