Euler’s Method for Approximating a Solution to a Differential Equation

Based on Euler’s Method, this interactive graph illustrates a numerical method for solving differential equations. This approach is at the core of many sophisticated computer models of physical phenomena (like climate and weather).

[inline]

  • Starting point: (x,y) = (   ,   )
  • Step size:
  • Direction:


Your browser does not support the canvas element.

  • Slope equation: dy/dx =   x +   
  • Show analytical solution:

If you know the equation for the slope of a curve (the red line for example),

and a point that the curve passes through, such as , you can integrate to find the equation of the curve:

[script type=”text/javascript”]

var width=500;
var height=500;
var xrange=10;
var yrange=10;

mx = width/(2.0*xrange);
bx = width/2.0;
my = -height/(2.0*yrange);
by = height/2.0;

function upause () {
ct = 1;
}

function draw_9110(ctx, polys) {
t_9110=t_9110+dt_9110;
//ctx.fillText (“t=”+t, xp(5), yp(5));
ctx.clearRect(0,0,width,height);

polys[0].drawAxes(ctx);
polys[0].slopeField(ctx, 1.0, 1.0, 0.5);
ctx.lineWidth=2;
polys[0].draw(ctx);
polys[0].write_eqn(ctx, “slope = dy/dx”);

eu = eu_9110;
//override nsteps values
//max_nsteps_9110 = eu.nsteps;
//eu.nsteps = nsteps_9110;

if (nsteps_9110 <= max_nsteps_9110) { nsteps_9110 = nsteps_9110 + 1;} else { nsteps_9110 = 0; } eu.nsteps = nsteps_9110; if (show_parabola_9110 == 1) { polys[0].Integrate(ctx, eu.x1, eu.y1); polys[0].integral.color = '#3c3'; polys[0].integral.draw(ctx); } pos = polys[0].EulerApprox(ctx, eu); //document.getElementById('comment_9110').innerHTML = eu.nsteps+ " " + eu.x1 + " " + eu.y1+ " " + eu.dx+ " " + eu.dir+ " " + max_nsteps_9110; //starting point label ctx.textAlign="right"; ctx.textBaseline="alphabetic"; ctx.font = "14pt Calibri"; ctx.fillStyle = "#00f"; ctx.fillText ('start = ('+eu.x1+','+eu.y1+")", xp(eu.x1-0.25), yp(eu.y1-0.5)); //pos = polys[0].EulerApprox(ctx, st_pt_x_9110, st_pt_y_9110, 0.5,20,-1); //pos = polys[0].EulerApprox(ctx, st_pt_x_9110, st_pt_y_9110, 1, 6,1); //draw starting point ctx.strokeStyle="#00f"; ctx.lineWidth=2; ctx.beginPath(); ctx.arc(xp(eu.x1),yp(eu.y1),dxp(0.1),0,Math.PI*2); ctx.closePath(); ctx.stroke(); //ctx.fillText (' c='+move_dir_9110+' '+polys[1].c, xp(5), yp(5)); } //init_mouse(); var c_9110=document.getElementById("myCanvas_9110"); var ctx_9110=c_9110.getContext("2d"); var change = 0.0001; function create_lines_9110 () { //draw line var polys = []; polys.push(addPoly(0, 0.5, -1)); return polys; } function set_max_nsteps_9110 (eu) { max_nsteps_9110 = 1.5*(xrange - eu.x1)/eu.dx; } var polys_9110 = create_lines_9110(); var x1=xp(-10); var y1=yp(1); var x2=xp(10); var y2=yp(1); var dc_9110=0.05; var t_9110 = 0; var dt_9110 = 500; var st_dx_9110 = 1.0; var st_nsteps_9110 = 0; var nsteps_9110 = 0; var max_nsteps_9110 = 20; var dir_9110 = 1; var show_parabola_9110 = 1; var st_pt_x_9110 = -2; var st_pt_y_9110 = -3; var move_dir_9110 = 1.0; // 1 for up //Form interaction function update_form_9110 (x, y, dx, nsteps, dir, b, c, show_para) { x_9110.value = st_pt_x_9110+""; y_9110.value = st_pt_y_9110+""; dx_9110.value = st_dx_9110.toPrecision(2); //c_max_nsteps_9110.value = max_nsteps_9110+""; //document.getElementById('comment_9110').innerHTML = c_dir_9110; if (dir == 1) { c_dir_9110.selectedIndex = 0; } else {c_dir_9110.selectedIndex = 1;} b_9110.value = b+""; c_9110.value = c+""; if (show_para == 1) { ctrl_show.checked = 1; } else {ctrl_show.checked = 0;} } function get_euler_form_9110 () { pos = {x : parseFloat(x_9110.value), y : parseFloat(y_9110.value)} if (c_dir_9110.selectedIndex == 0) { dir = 1;} else {dir = -1;} eu = addEuler(pos.x, pos.y, parseFloat(dx_9110.value), max_nsteps_9110, dir); return eu } function update_solution_9110 (ctx, eu) { //update equations and solution document.getElementById('slope_eqn_9110').innerHTML = polys_9110[0].get_eqn("dy/dx"); //document.getElementById('eqn_9110').innerHTML = "hey"+eu.y1; polys_9110[0].Integrate(ctx, eu.x1, eu.y1); document.getElementById('eqn_9110').innerHTML = polys_9110[0].integral.get_eqn(); document.getElementById('init_pt_9110').innerHTML = "("+eu.x1+","+eu.y1+")"; } function add_euler_line() { eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } var x_9110 = document.getElementById("x_9110"); var y_9110 = document.getElementById("y_9110"); var dx_9110 = document.getElementById("dx_9110"); //var c_max_nsteps_9110 = document.getElementById("nsteps_9110"); var c_dir_9110 = document.getElementById("dir_9110"); var b_9110 = document.getElementById("b_9110"); var c_9110 = document.getElementById("c_9110"); var ctrl_show = document.getElementById("chk_show_para_9110"); update_form_9110(x_9110, y_9110, dx_9110, nsteps_9110, dir_9110, polys_9110[0].b, polys_9110[0].c, show_parabola_9110); var eu_9110 = get_euler_form_9110 (); set_max_nsteps_9110(eu_9110); update_solution_9110(ctx_9110, eu_9110); setInterval("draw_9110(ctx_9110, polys_9110)", dt_9110); x_9110.onchange = function() { eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } y_9110.onchange = function() { eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } c_dir_9110.onchange = function() { eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } dx_9110.onchange = function() { eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; //max_nsteps_9110 = 1.4 * (10 - eu_9110.x1)/eu_9110.dx; set_max_nsteps_9110(eu_9110); nsteps_9110 = 0; } b_9110.onchange = function() { polys_9110[0].set_b(parseFloat(this.value)); eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } c_9110.onchange = function() { polys_9110[0].set_c(parseFloat(this.value)); eu_9110 = get_euler_form_9110 (); max_nsteps_9110 = eu_9110.nsteps; nsteps_9110 = 0; } ctrl_show.onchange = function() { if (this.checked == 1) { show_parabola_9110 = 1;} else {show_parabola_9110 = 0} } [/script] [/inline]

If you don’t have a starting point (initial condition), you can draw a slope field to see what the general pattern of all the possible solutions.

Even with a starting point, however, there are just times when you can’t integrate the slope equation — it’s either too difficult or even impossible.

Then, what you can do is come up with an approximation of what the curve looks like by projecting along the slope from the starting point.

The program above demonstrates how it’s done. This approach is called Euler’s Method, and is gives a numerical approximation rather than finding the exact, analytical solution using calculus (integration).

So why use an approximation when you can find the exact solution? Because, there are quite a number of problems that are impossible or extremely difficult to solve analytically, things like: the diffusion of pollution in a lake; how changing temperature in the atmosphere gives you weather and climate; the flow of groundwater in aquifers; stresses on structural members of buildings; and the list goes on and on.

As with most types of numerical approximations, you get better results if you can reduce the step size between projections of the slope. Try changing the numbers and see.

A more detailed version, with solutions, is here: Euler’s Method.

A good reference: Euler’s Method by Paul Dawkins.

Solving Quadratic Equations

So here we have a little grapher that solves quadratic equations visually. Just enter the coefficients in the equation.

[inline]

Quadratic Equation:       y = a x2 + b x + c

Enter:                              
y =
x2 +
x +


Your browser does not support the canvas element.

Solution:

Analytical solution by factoring (with a little help from the quadratic equation if necessary).

[script type=”text/javascript”]
var width=500;
var height=500;
var xrange=10;
var yrange=10;

mx = width/(2.0*xrange);
bx = width/2.0;
my = -height/(2.0*yrange);
by = height/2.0;

function draw_9114(ctx, polys) {
t_9114=t_9114+dt_9114;
//ctx.fillText (“t=”+t, xp(5), yp(5));
ctx.clearRect(0,0,width,height);

polys[0].drawAxes(ctx);
ctx.lineWidth=2;
polys[0].draw(ctx);
polys[0].write_eqn(ctx);

//polys[0].y_intercepts(ctx);
polys[0].x_intercepts(ctx);
//write intercepts on graph

ctx.fillText (‘x intercepts: (when y=0)’, xp(9), yp(8));
if (polys[0].x_intcpts.length > 0) {
line = “0 = “;
for (var i=0; i 0.0) { sign=”-“;} else {sign=”+”;}
line = line + “(x “+sign+” “+ Math.abs(polys[0].x_intcpts[i].toPrecision(2))+ “)”;
}
ctx.fillText (line, xp(9), yp(7));
// if (polys[0].order == 2 ) {
// if (polys[0].x_intcpts[0] > 0.0) { sign1=”-“;} else {sign1=”+”;}
// if (polys[0].x_intcpts[1] > 0.0) { sign2=”-“;} else {sign2=”+”;}
// ctx.fillText (‘0 = (x ‘+sign1+” “+Math.abs(polys[0].x_intcpts[0])+”) (x “+sign2+Math.abs(polys[0].x_intcpts[1])+”)”, xp(9), yp(7));
// }
for (var i=0; i2 “+polys[0].bsign+” “+Math.abs(polys[0].b.toPrecision(2))+” x “+ polys[0].csign+” “+Math.abs(polys[0].c.toPrecision(2))+”

“;

solution = solution + ‘Factoring:      ‘;

if (polys[0].x_intcpts.length > 0) {
solution = solution + ‘0 = ‘;
for (var i=0; i 0.0) { sign=”-“;} else {sign=”+”;}
solution = solution + “(x “+sign+” “+ Math.abs(polys[0].x_intcpts[i].toPrecision(2))+ “)”;
}
solution = solution + ‘

‘;
solution = solution + ‘Set each factor equal to zero:
     ‘;
for (var i=0; i 0.0) { sign=”-“;} else {sign=”+”;}
solution = solution + “x “+sign+” “+ Math.abs(polys[0].x_intcpts[i].toPrecision(2))+ ” = 0           “;
}
solution = solution + ‘

and solve for x:
     ‘;
for (var i=0; i‘;
}
document.getElementById(‘equation_9114’).innerHTML = solution;
}

else if (polys[0].order == 1) {
solution = solution + ‘
     ‘;
solution = solution + “y = “+” “+Math.abs(polys[0].b.toPrecision(2))+” x “+ polys[0].csign+” “+Math.abs(polys[0].c.toPrecision(2))+”

“;
solution = solution + ‘

Set y=0 and solve for x:
     ‘;
solution = solution + ” 0 = “+” “+Math.abs(polys[0].b.toPrecision(2))+” x “+ polys[0].csign+” “+Math.abs(polys[0].c.toPrecision(2))+”

“;
solution = solution + ‘     ‘;
solution = solution + (-1.0*polys[0].c).toPrecision(2) +” = “+” “+Math.abs(polys[0].b.toPrecision(2))+” x “+”

“;
solution = solution + ‘     ‘;
solution = solution + (-1.0*polys[0].c).toPrecision(2)+”/”+polys[0].b.toPrecision(2)+” = “+” x “+”

“;
solution = solution + ‘     ‘;
solution = solution + “x = “+ (-1.0*polys[0].c/polys[0].b).toPrecision(4)+”

“;

document.getElementById(‘equation_9114’).innerHTML = solution;
}

}

//init_mouse();

var c_9114=document.getElementById(“myCanvas_9114”);
var ctx_9114=c_9114.getContext(“2d”);

var change = 0.0001;

function create_lines_9114 () {
//draw line
//document.write(“hello world! “);
var polys = [];
polys.push(addPoly(1,6, 5));

// polys.push(addPoly(0.25, 1, 0));
// polys[1].color = ‘#8C8’;

return polys;
}

var polys_9114 = create_lines_9114();

var x1=xp(-10);
var y1=yp(1);
var x2=xp(10);
var y2=yp(1);
var dc_9114=0.05;

var t_9114 = 0;
var dt_9114 = 100;
//end_ct = 0;
var st_pt_x_9114 = 2;
var st_pt_y_9114 = 1;

var move_dir_9114 = 1.0; // 1 for up

var a_coeff_9114 = document.getElementById(“a_coeff_9114″);
a_coeff_9114.value = polys_9114[0].a+””;
var b_coeff_9114 = document.getElementById(“b_coeff_9114″);
b_coeff_9114.value = polys_9114[0].b+””;
var c_coeff_9114 = document.getElementById(“c_coeff_9114″);
c_coeff_9114.value = polys_9114[0].c+””;

//document.write(“test= “+c_coeff_9114.value+” “+polys_9114[0].c);
//setInterval(“draw_9114(ctx_9114, polys_9114)”, dt_9114);
draw_9114(ctx_9114, polys_9114);

a_coeff_9114.onchange = function() {
polys_9114[0].set_a(parseFloat(this.value));
draw_9114(ctx_9114, polys_9114);
}
b_coeff_9114.onchange = function() {
polys_9114[0].set_b(parseFloat(this.value));
draw_9114(ctx_9114, polys_9114);
}
c_coeff_9114.onchange = function() {
polys_9114[0].set_c(parseFloat(this.value));
draw_9114(ctx_9114, polys_9114);
}

[/script]

[/inline]

Hopefully, this can help students learn about factoring and quadratics in a more graphical way.

Notes:

This is my first interactive post. I use Javascript in combination with HTML5. Now I need to figure out how to interact with the image itself, instead of the textboxes.

The CDC on Zombie Preparedness

Poster from the CDC.

The Centers for Disease Control (CDC) has been thinking about the potential for a zombie apocalypse. You can find a page on Zombie Preparedness on their website, as well as a graphic novel (9Mb pdf).

If you are generally well equipped to deal with a zombie apocalypse you will be prepared for a hurricane, pandemic, earthquake, or terrorist attack.

— Ali Khan (2011) (Head of the Office of Public Health Preparedness and Response at the Centers for Disease Control and Prevention): Quoted in Zombie Preparedness on the CDC website.

NOTE: The CDC recommends you quarantine zombies rather than kill them; Kyle Munkittrick, of the Pop Bioethics blog disagrees.

Slope Fields

[inline]

Your browser does not support the canvas element.

[script type=”text/javascript”]
var width=500;
var height=500;
var xrange=10;
var yrange=10;

mx = width/(2.0*xrange);
bx = width/2.0;
my = -height/(2.0*yrange);
by = height/2.0;

function draw9083(ctx, polys) {
t=t+dt;
//ctx.fillText (“t=”+t, xp(5), yp(5));
ctx.clearRect(0,0,width,height);

polys[0].drawAxes(ctx);
polys[0].slopeField(ctx, 1.0, 1.0, 0.5);
ctx.lineWidth=2;
polys[0].draw(ctx);
polys[0].write_eqn(ctx, “dy/dx “);

}

//init_mouse();

var c9083=document.getElementById(“myCanvas9083”);
var ctx9083=c9083.getContext(“2d”);

var change = 0.0001;

function create_lines9083 () {
//draw line
//document.write(“hello world! “);
var polys = [];
polys.push(addPoly(0, 0.5, 1));

return polys;
}

var polys9083 = create_lines9083();

var x1=xp(-10);
var y1=yp(1);
var x2=xp(10);
var y2=yp(1);
var dy=0.01;

var t = 0;
var dt = 50;
end_ct = 0;

var move_dir = 1; // 1 for up

//draw9083();
//document.write(“x”+x2+”x”);
//ctx9083.fillText (“n=”, xp(5), yp(5));
setInterval(“draw9083(ctx9083, polys9083)”, dt);

[/script]
[/inline]

Say you have the equation that gives you the slope of a curve (let  \frac{dy}{dx} ) be the slope):
! \frac{dy}{dx} = \frac{1}{2} x + 1

When you use integration to solve the equation, there are quite the number of possible solutions (infinite actually), because when you integrate:

! y = \int (\frac{1}{2} x + 1 ) dx

you get:
! y = \frac{1}{4} x^2 + x + c

where c is a constant. Unfortunately, you don’t know what c is without more information; it could be anything.

However, even without integrating, we can get a feel for what the curve will look like by plotting what the slope will look like at a bunch of different points in space. This comes in really handy when you end up with a equation for slope that is really hard — or even impossible — to solve.

The graph below show a curve of possible solutions to the slope equation. You should be able to see, as the graph slowly moves up and down, how the slope of the graph corresponds to the slope field.

[inline]

Your browser does not support the canvas element.

[script type=”text/javascript”]
var width=500;
var height=500;
var xrange=10;
var yrange=10;

mx = width/(2.0*xrange);
bx = width/2.0;
my = -height/(2.0*yrange);
by = height/2.0;

function draw9083b(ctx, polys) {
t9083b=t9083b+dt9083b;
//ctx.fillText (“t=”+t, xp(5), yp(5));
ctx.clearRect(0,0,width,height);

polys[0].drawAxes(ctx);
polys[0].slopeField(ctx, 1.0, 1.0, 0.5);
ctx.lineWidth=2;
polys[0].draw(ctx);
polys[0].write_eqn(ctx, “dy/dx “);

if (polys[1].c > yrange) {
move_dir9083b = -1.0;
polys[1].c = yrange;
}
else if (polys[1].c < -yrange) { move_dir9083b = 1.0; polys[1].c = -yrange; } polys[1].c = polys[1].c + dc9083b*move_dir9083b; polys[1].draw(ctx); polys[1].write_eqn(ctx); //ctx.fillText (' c='+move_dir9083b+' '+polys[1].c, xp(5), yp(5)); } //init_mouse(); var c9083b=document.getElementById("myCanvas9083b"); var ctx9083b=c9083b.getContext("2d"); var change = 0.0001; function create_lines9083b () { //draw line //document.write("hello world! "); var polys = []; polys.push(addPoly(0, 0.5, 1)); polys.push(addPoly(0.25, 1, 0)); polys[1].color = '#8C8'; return polys; } var polys9083b = create_lines9083b(); var x1=xp(-10); var y1=yp(1); var x2=xp(10); var y2=yp(1); var dc9083b=0.05; var t9083b = 0; var dt9083b = 100; //end_ct = 0; var move_dir9083b = 1.0; // 1 for up //draw9083b(); //document.write("x"+x2+"x"); //ctx9083b.fillText ("n=", xp(5), yp(5)); setInterval("draw9083b(ctx9083b, polys9083b)", dt9083b); [/script] [/inline]

Straight Lines

[inline]

Your browser does not support the canvas element.

[script type=”text/javascript”]
var width=500;
var height=500;
var xrange=10;
var yrange=10;

mx = width/(2.0*xrange);
bx = width/2.0;
my = -height/(2.0*yrange);
by = height/2.0;

function draw8587(ctx, polys) {
t=t+dt;
//ctx.fillText (“t=”+t, xp(5), yp(5));
ctx.clearRect(0,0,width,height);

polys[0].drawAxes(ctx);
//drawAxes(ctx);
ctx.lineWidth=3;
//ctx.beginPath();
//ctx.moveTo(x1,y1);
//ctx.lineTo(x2,y2);
//ctx.stroke();
//ctx.closePath();
//y2+=dyp(dy);

// add polynomial timing
// First move (from y=0 to y=1)
if (polys[0].c < 1.0) { polys[0].c = polys[0].c + 2.0*dt*change*move_dir; } // Second move (change from y=1 to y=0.5x+1) else if (polys[0].b < 0.5) { polys[0].b = polys[0].b + dt*change*move_dir; polys[0].order = 1; } // Third move (change from y=0.5x+1 to y=0.5x+4) else if (polys[0].c < 4.0) { polys[0].c = polys[0].c + 2.*dt*change*move_dir; //polys[0].order = 1; } else { //move_dir = move_dir * -1; end_ct = end_ct + dt; if (end_ct >= dt*40) {
polys[0].a = 0;
polys[0].b = 0;
polys[0].c = 0;
polys[0].order = 0;
}
}
ctx.textAlign=”center”;
ctx.textBaseline=”alphabetic”;
ctx.font = “14pt Arial”;
ctx.fillStyle = ‘#f00’;
ctx.fillText (“Equation of a Line”, xp(-5), yp(9));
ctx.fillText (“y = mx + b”, xp(-5), yp(5));
ctx.font = “12pt Arial”;
//ctx.fillText (“y = “+polys[0].b.toPrecision(1)+”x + “+ polys[0].c.toPrecision(2), xp(-5), yp(7));
ctx.fillText (“b = intercept = “+polys[0].c.toPrecision(2), xp(-7), yp(3));

//draw polynomials
ctx.lineWidth=3;
for (var i=0; i

Here’s a prototype for showing how the equation of a straight line changes as you change its slope (m) and intercept (b):
! y = mx + b

It starts with an horizontal line along the x-axis, where the slope is zero and the intercept is zero:
! y = 0.0

The line moves upward, but stays horizontal, until the intercept is 1.0:
! y = 1.0

The slope increases from horizontal (m = 0), gradually, until the slope is 0.5:
! y = 0.5x + 1.0

Finally, we move the line upwards again, without changing the slope (m = 0.5), until the intercept is equal to 4.0:
! y = 0.5x + 4.0

Now I just need to figure out how to make them interactive.

Numerical Integration

This is an attempt to illustrate numerical integration by animating an HTML5’s canvas.

We’re trying to find the area between x = 1 and x = 5, beneath the parabola:

 y = -\frac{1}{4} x^2 + x + 4

By integrating, the area under the curve can be calculated as being 17 ⅔ (see below for the analytical solution). For numerical integration, however, the area under the curve is filled with trapezoids and the total area is calculated from the sum of all the areas. As you increase the number of trapezoids, the approximation becomes more accurate. The reduction in the error can be seen on the graph: with 1 trapezoid there is a large gap between the shaded area and the curve; more trapezoids fill in the gap better and better.

The table below show how the error (defined as the difference between the calculation using trapezoids and the analytic solution) gets smaller with increasing numbers of trapezoids (n).

Number of trapezoids Area (units2) Error (difference from 17.66)
1 15.00 2.66
2 17.00 0.66
3 17.37 0.29
4 17.50 0.16
5 17.56 0.10
6 17.59 0.07
7 17.61 0.05
8 17.63 0.03
9 17.63 0.03
10 17.64 0.02

Analytic solution

The area under the curve, between x = 1 and x = 5 can be figured out analytically by integrating between these limits.

 Area = \int_{_1}^{^5} \left(-\frac{x^2}{4}  + x + 4 \right) \,dx

 Area = \left[-\frac{x^3}{12}  + \frac{x^2}{2} + 4x \right]_1^5

 Area = \left[-\frac{(5)^3}{12}  + \frac{(5)^2}{2} + 4(5) \right] - \left[-\frac{(1)^3}{12}  + \frac{(1)^2}{2} + 4(1) \right]

 Area = \left[-\frac{125}{12}  + \frac{25}{2} + 16 \right] - \left[-\frac{1}{12}  + \frac{1}{2} + 4 \right]

 Area = \left[ 22 \frac{1}{12} \right]  - \left[4 \frac{5}{12} \right]

 Area = 17 \frac{2}{3}  = 17.6\bar{6}

(See also WolframAlpha’s solution).

Update

Demonstration of this numerical integration using four trapezoids in embeddable graphs.