A Non-Trivial Examples: Complex Numbers
As mentioned in Chapter 2 one of the features needed for serious scientific computation is complex numbers. Unfortunately no popular computer language other than Fortran provides them as a built-in data type. (Actually this is such a common and useful example and was used by so many textbooks that it was recently added to the C++ standard library which makes it far less useful as an example. Fortunately, however, Java has not yet been around long enough to have all its really useful examples coopted into the standard library.) Let’s see how we might implement them in Java. From the standpoint of a data type you really don’t need much. Mathematically a complex number is composed of a real part u and an imaginary part v. We can create such a class in the following way:
public class ComplexNumber extends Object {
public double u;
public double v;
}
While this is sufficient to encompass all the data that one needs in a complex number it’s not a very good example of object-oriented programming. To actually do anything with this number we have to know exactly how the data structure is defined. If we change the data structure, for instance by defining a complex number in terms of it’s magnitude r and its argument theta instead of by its real and imaginary components we have to change all the code that depends on it.
We also have to write code to explicitly add the numbers, multiply them or do anything else we might need to do with complex numbers. If we need to add complex numbers in more than one place, then we need to write the addition code again, or, at the very least, copy and paste it.
A better implementation of a complex number class will shield us from the exact storage of the data, i.e. x and y vs. r and theta. It will also provide methods that let us perform any operation we might need to perform on or with a complex number.
Before writing code we need to ask ourselves what we’ll do with a complex number. Most objects first require a constructor, a method that is called when you create a new complex number. A more complicated object may also require a destructor method that’s called when you get rid of an object; but since this is a fairly simple object, we’ll let Java’s built-in garbage collection take care of that for us.
Since these are complex numbers it’s not unlikely that we’ll need to add them, subtract them, multiply them and divide them. We’ll also want to be able to access their real and imaginary parts as well as their absolute values and arguments. The following class does all that.
//
public class Complex extends Object {
private double u;
private double v;
Complex (double x, double y) {
u=x;
v=y;
}
public double Real () {
return u;
}
public double Imaginary () {
return v;
}
public double Magnitude () {
return Math.sqrt(u*u + v*v);
}
public double Arg () {
return Math.atan2(v, u);
}
// Add z to w; i.e. w += z
public Complex Plus (Complex z) {
return new Complex(u + z.u, v + z.v);
}
// Subtract z from w
public Complex Minus (Complex z) {
return new Complex(u – z.u, v – z.v);
}
public Complex Times (Complex z) {
return new Complex(u*z.u – v*z.v, u*z.v + v*z.u);
}
// divide w by z
public Complex DivideBy (Complex z) {
double rz = z.Magnitude();
return new Complex((u * z.u + v * z.v)/(rz*rz),(v * z.u – u * z.v)/(rz*rz));
}
}
Notice especially that u and v are now private. They cannot be accessed by external code even if we want them to be.
The use of one of these methods will look like the following. Add the following ComplexExamples class to the Complex.java file and compile. Then run ComplexExamples in the usual way by typing java ComplexExamples.
//Complex Arithmetic Examples
class ComplexExamples {
public static void main (String args[]) {
Complex u, v, w, z;
u = new Complex(1,2);
System.out.println(”u: ” + u.Real() + ” + ” + u.Imaginary() + “i”);
v = new Complex(3,-4.5);
System.out.println(”v: ” + v.Real() + ” + ” + v.Imaginary() + “i”);
// Add u + v;
z=u.Plus(v);
System.out.println(”u + v: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
// Add v + u;
z=v.Plus(u);
System.out.println(”v + u: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=u.Minus(v);
System.out.println(”u – v: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=v.Minus(u);
System.out.println(”v – u: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=u.Times(v);
System.out.println(”u * v: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=v.Times(u);
System.out.println(”v * u: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=u.DivideBy(v);
System.out.println(”u / v: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
z=v.DivideBy(u);
System.out.println(”v / u: “+ z.Real() + ” + ” + z.Imaginary() + “i”);
}
}
Exercises
1. What happens if we try to add a complex number to itself? e.g.
z = u.Add(u);
How about if we multiply, divide or subtract? e.g.
z = u.Multiply(u);
z = u.Divide(u);
z = u.Minus(u);
2. Rewrite the Complex class so that it stores its data as r and theta rather than u and v. Be sure to be careful at zero.
3. Add PlusEqual, MinusEqual, DivideEqual and MultiplyEqual methods to the Complex class that mimic the behavior of the +=, -=, *= and /= operators.
4. Add an equality method to the Complex class that tests whether two complex numbers are equal and returns a boolean.
5. For math whizzes only: Explain why it would not be a good idea to add less than or greater than methods to the Complex class.
6. For math whizzes only: Add a logarithm method to the Complex number class. Pick the branch between zero and 2pi.
7. For math whizzes only: Add a power method to the complex number class. This is straightforward for real powers. For a real challenge allow arbitrary complex powers. Be sure to consider how you’ll deal with branch cuts.