T u t o

advertisement
Tutorial 7
Topics
Numerical analysis in Java
Root Finding
Integration
Tutorial Exercises
Design Exercises
Numerical analysis in Java
Numerical Analysis is a branch of applied mathematics which devises and evaluates
techniques for employing computers to solve numerical problems, and to study their
convergence and errors.
Consider writing a function that finds a root of a function, f(x) = 0, in some interval
[a,b]. This root finding method should have the function f(x) as an argument. In Java,
since (almost) everything has to be an object, this is done by wrapping the function
you want to pass in an object. You can then pass the object as an argument to the
method which finds a root.
This wrapping is usually done by declaring an interface that describes an object which
contains a function. An interface is like a model that specifies one of more capabilities
(methods) that must be defined in every class that "implements" it. Only function
prototypes can be defined in interfaces (the bodies of these methods are then defined
in the classes that implement the particular interface).
For instance, let's declare the following interface:
interface MathFunction
{
public double func(double x);
}
We can then use this interface to declare a class with a particular function: class
Function1 implements MathFunction
{
public double func(double x)
{
return x * x - 2; //the definition for a particular function,
//here, for instance, represent f(x) = x*x-2;
}
}
So, now, we would define our root finding function as follows: public class RootFinder
{
public static double rootFindingMethod( MathFunction mathf, ...)
{
// finding the root for a particular function
...
}
}
... and we would call the function as follows with Function1: double rootResult =
RootFinder.rootFindingMethod(new Function1(), ...);
For instance, the following code will find the root for Function1( f(x) = x2-2) by using
the bisect method defined in the next section (suppose the bisect method is defined in
the class RootFinder): // main() method in class RootFinder
public static void main(String[] args) {
double root= RootFinder.bisect(new Function1(), -1.0, 2.0, 0.0001);
System.out.println("Root: " + root);
System.exit(0);
} }
Root finding
There are four "elementary" methods for root finding. Of these, bisection and
Newton's method can be used to solve actual problems in some circumstances. Secant
and regula falsi are only of pedagogical value. See "Numerical Recipes in C" for
discussions of these and other methods if you actually need to find roots; the first few
pages of text on each method are relatively readable and illuminating.
For the bisection, secant and regula falsi methods, we give the reasoning behind each
and a snippet of pseudo-code that implements it. This code is illustrative only; it is
not robust nor should it be used for actual problems.
Bisection
Start with an interval known to bracket a root.
Divide it in half, reducing the interval to a half known to contain a root.
Do this until the interval is small enough
public static double bisect( MathFunction f, double x1, double x2, double epsilon)
{
double m;
for (m= (x1+x2)/2.0; Math.abs(x1-x2)> epsilon; m= (x1+x2)/2.0)
if (f.func(x1)*f.func(m) <= 0.0)
x2= m;
// Use left subinterval
else
x1= m;
// Use right subinterval
return m;
}
Secant
One of the root finding methods, the Secant method, begins with an interval known to
contain a root. Call the endpoints of this interval x1 and x2. The method works by
fitting a straight line between the two current points (x1, f(x1)) and (x2, f(x2)) and
finding the point x* where that line intersects the x axis. The end point x1 is reset to
x2's value. The point x* becomes the new endpoint (x2). This procedure continues
until either the interval is very small, or we find a root of the function. The following
is the code for implementing the method.
start with x1 and x2
fit a straight line between f(x1) and f(x2)
find x*, the root of this straight line
if x* is close to x2, it returns the value as the root of the original equation
If not, it resets the value of x1 to x2 and x2 to x* and tries the method again.
public static double secant(MathFunction f, double x1, double x2, double epsilon)
{
double root = x1;
double y1, y2;
while (Math.abs(x2 - x1) > epsilon)
{
y1 = f.func(x1);
y2 = f.func(x2);
root = x1-y1*(x2-x1)/(y2-y1);
x1 = x2;
x2 = root;
}
return root;
}
Regula Falsi
The Regula Falsi method is almost the same as the Secant method. The difference is
when the point x* becomes one of the endpoints of the next interval, the other
endpoint is whichever of x1 and x2 ensures that the new interval still contains a root
of the function, i.e., the Regula Falsi mehtod chooses the 'previous' point to keep the
zero bracketed instead of always using just the last two points.
start with x1 and x2
fit a straight line between f(x1) and f(x2)
find x*, the root of this straight line
if f(x*) is close to 0, it returns the value x* as the root of the original equation
If not, it resets (x1, x2) to (x1, x*) or (x*, x2) (the new interval that contains a
root of the function) and tries the method again.
public static double regulaFalsi(MathFunction f, double x1, double x2, double epsilon)
{
double y1, y2, root, yroot;
do {
y1 = f.func(x1);
y2 = f.func(x2);
root = x1-y1*(x2-x1)/(y2-y1);
yroot = f.func(root);
if (yroot*y2 < 0.0)
x1=root;
else
x2=root;
} while (Math.abs(yroot)>epsilon);
return root;
}
Newton
Start at some point x on the x-axis
fit a line tangent to f(x) at x
solve for the point y where the tangent intersects the x-axis
do this until |x-y| < epsilon, then return y
The code can be found in the lecture notes.
Integration
Numerical integration is used in the calculation of definite integrals. Elementary
methods for numerical integration are based on the summation of small areas (slices)
under a function's curve and between two limits x1and x2.
Depending on the way the area of each segment is computed, we have the following
elementary or pedagogical methods:
Rectangular rule
The method is based on the areas of rectangles. The area of each segment is
computed as:
or
where
and A is the area of
segment.
Trapezoidal rule
This method is as simple as the rectangular rule but is often more accurate. It is based
on the areas of trapezoids. The area of each segment A is computed as:
Simpson's rule
Simpson's rule uses 3 points to approximate the function with a second order
polynomial. The area of each segment is computed as:
Simpson's rule is a commonly taught method for numerical integration of definite
integrals. It approximates the function by fitting parabolas to each segment, then
summing the areas under each segment to calculate the area under the curve. The
following code implements an elementary version of Simpson's rule:
// Simpson's Rule
public static double simpson(MathFunction f, double a, double b, int nPanels)
{
double answer = 0;
double x = a;
double h = (b - a) / nPanels;
for(int i=1; i <= nPanels; i++)
{
answer += (f.func(x) + 4 * f.func(x + h / 2) + f.func(x + h)) / 6;
x = a + i * h;
}
return h * answer;
}
To solve real problems, you should use extended Simpson's rule, which is the simplest
usable version. The extended Simpson's method is covered in the Lecture Notes 20.
Tutorial Exercises
1. Given two functions, f(x) and g(x), how can we determine the value of x where
f(x) = g(x)?
2. How do we determine if points x1 and x2 bracket the root of function f(x)?
3. When are the root finding methods used? When are numerical integration
methods used?
4. Trace the bisection method for the function f(x) = x2 - 5x +4 starting from a=0
and b=3 for a couple of iterations.
5. Why do we use the derivative of f(x) when we're using Newton's method to find
the root of f(x)?
Design Exercises
Design the following three classes: RootFinder, Integration, and NumericalTest.
RootFinder will implement elementary versions of the three root finding
methods (Bisect, Secant, and Regula Falsi from the tutorial notes, not the
lecture notes).
Integration will implement elementary versions of the three integration
methods (Rectangular, Trapezoidal, and Simpson).
NumericalTest will have a main method to test the three root-finding methods
by finding the root for:
f1(x) = x5 + 4x3 + 7x - 10; (finding the root in the range from -1.0 to 2.0, using
epsilon= 0.0001)
and test the three integration methods by integrating for the function:
f2(x) = x6 + 7x5 + 3x4 + x2 - 5x +4; (integrating from -5.0 to 8.0, using n =
1000)
Execute your program and compare the result of these methods. Hint: You will have
to create two classes to represent f1(x) and f2(x). Create these classes by
implementing the MathFunction interface.
Download