Upper and Lower Riemann Sums

We define some basic functions to compute upper and lower riemansums,
and to plot them. You can click on code to see the actual code.

Let f(x) be a continous function of one variable, then we wish to
study the upper and lower riemann sums for a partition. A partition
is a subdivision of an interval into a number of subintervals, and can
be specified by the list of end points of the subintervals. Roughly
speaking the Riemann Sum is the sum of the product of the length of
each subinterval with the value of the function on that subinterval.

To calculate an upper sum we must find the maximum of a function on
each of the little subintervals of the partition. We know how to do
this if the function is differentiable, by first finding the
critical points:

critical_points(expr,var):= block([numer:true,f:diff(expr,var)], realroots(num(f)*denom(f),10^-7));

where realroots finds the real roots of a single variable
polynomial to a given precision, and diff takes the derivative. The
above will work for rational functions, since in general we need to
include as critical points roots of the numerator and denominator.

Now we do a simple example:

upper_sum(x^3-4*x+1,x,[0,.3,.5,.8,.9,1.8,1.9,2.0])
returns - 0.37119999999999953

How close is this to the true answer...which should be the sum with
VERY small (infinitely small!) intervals. We wish to form the lower
riemann sum as well and to compare it to the upper riemman_sum. The
true answer must lie somewhere between the upper and lower sums. The next
function returns a list containing the upper sum, the lower sum, and
their difference.

upper_and_lower_sums(expr,var,partition):= block([up:upper_sum(expr,var,partition), low:-upper_sum(-expr,var,partition)], [up,low,up-low]);

upper_and_lower_sums(x^3-4*x+1,x, partition:[0,.3,.5,.8,.9,1.2,1.3,1.5,1.8,1.9,2.0])

produces [- 1.2369999999999999, - 2.6503604307034014, 1.4133604307034016]

You see the upper is much larger than the lower sum. After clicking
above, the value of partion will be set to whatever you set, and so
now you may look at the graph:

graph_riemann_sum(upper,x^3-4*x+1,x,partition)

graph_riemann_sum(lower,x^3-4*x+1,x,partition)

You can see by the graphs why the difference is so large.

You can try refining the partition by adding more items to it.
Each time you add an item the two sums get closer, with the upper one
getting smaller and the lower one getting bigger. Can you explain
this?
upper_and_lower_sums(x^3-4*x+1,x, [0,.3,.5,.7,.8,.9,1.2,1.4,1.5,1.8,1.9,2.0])

yields [- 1.3036000000000001, - 2.5963604307034012, 1.2927604307034011]

They are still quite far apart. It is tedious to add the partition
elements by hand, and it also will give an incorrect answer if we
accidentally misorder them!. The following function makes a partition
into n evenly spaced sub intervals.

make_partition(a,b,n):=makelist(a+(b-a)*i/n,i,0,n);

Trying with a partition with a larger number of elements,

upper_and_lower_sums(x^3-4*x+1,x,make_partition(0.0,2.0,60));

returns [- 1.8962654320987655, - 2.1015289367448222, 0.20526350464605669]

If we now look at the graphs done with the new partitions we see
why the answer is now correct to within about 5%.

graph_riemann_sum(upper,x^3-4*x+1,x,make_partition(0.0,2.0,60))

graph_riemann_sum(lower,x^3-4*x+1,x,make_partition(0.0,2.0,60))

The exact answer can be calculated by antidifferentiation:
integrate(x^3-4*x+1,x,0,2) gives -2