Introduction
One of the more interesting types introduced in .NET Framework 4.0 is the
Complex structure which models the mathematical entity known as a 'complex
number'. This is a number of the form a + bi where i represents the square root
of -1.
The Complex structure lives in the System.Numerics namespace and, to use it, you
need to add a reference to System.Numerics.dll to your project.
The implementation is reasonably complete with the standard arithmetic operators
(+, -, *, /) being overloaded to work with complex numbers and there are
implicit conversions from all the standard arithmetic types (int, long, double
etc.). Construction using polar co-ordinates is supported and there is also a
full set of mathematical functions defined on complex numbers such as
trigonometric, absolute value, conjugate, reciprocal, power and square root.
So how can we use this type to solve a quadratic equation?
Solving quadratic equations (a first attempt)
One of the reasons why complex numbers were introduced into mathematics in the
first place is so that the quadratic equation:
ax^2 + bx + c = 0 // where a, b and c are real numbers, a is non-zero and ^ denotes the power function.
always has a solution and, indeed, exactly two solutions or roots as they are
called. For example, the equation:
x^2 + 4 = 0
has no real roots but it does have the complex roots 2i and -2i.
In general, the roots of any quadratic equation can be found from the formulas:
(-b + Sqrt (b^2- 4ac))/ 2a and (-b - Sqrt (b^2 - 4ac))/ 2a
It can be seen from these formulas that if b^2 - 4ac (known as the discriminant)
is negative then the roots will be complex rather than real numbers
So, on the face of it, we should be able to solve such equations with the
following C# program:
using
System;
using
System.Numerics;
class
Test
{
static void
Main()
{
// as an example let's solve x^2 + 4 = 0
Tuple<Complex,
Complex> roots = SolveQuadratic(1, 0, 4);
Console.WriteLine("The
roots are {0} and {1}", roots.Item1, roots.Item2);
Console.ReadKey();
}
static Tuple<Complex,
Complex> SolveQuadratic(double
a, double b, double
c)
{
if (a == 0)
throw new
ArgumentException("The coefficient of x
squared can't be zero");
double discriminant = b * b - 4.0 * a *
c;
Complex temp =
Complex.Sqrt(discriminant);
Complex root1 = (-b + temp) / (2.0 *
a);
Complex root2 = (-b - temp) / (2.0 *
a);
return Tuple.Create(root1,
root2);
}
}
Notice that we're using the generic Tuple class which is another new feature of
.NET 4.0. This enables us to return multiple values from a method without the
need for 'out' parameters or defining a custom type.
However, when we examine the output of this program, we see two problems:
The roots are (1.22460635382238E-16, 2) and (-1.22460635382238E-16, -2)
Firstly, the roots have a tiny real component and secondly the output is
expressed in Cartesian form (like points on the plane) and not in the more
familiar a + bi format.
So what can we do about these problems?
Solving quadratic equations (an improved version)
Clearly, the first problem is caused by the Complex.Sqrt method producing
anomalous results. The obvious way to solve this is to use the Math.Sqrt method
instead and, if the discriminant is negative, multiply the result by the square
root of -1.
As far as I can see, there is no support for the a + bi format in the
Complex.ToString method or anywhere else so we need to write a custom method to
deal with this. This gives us the following improved version of the program:
using
System;
using
System.Numerics;
class
Test
{
static void
Main()
{
// as an example let's solve x^2 + 4 = 0
Tuple<Complex,
Complex> roots = SolveQuadratic(1, 0, 4);
Console.WriteLine("The
roots are {0} and {1}", ShowComplex(roots.Item1),
ShowComplex(roots.Item2));
// and also x^2 - 2x + 2
roots = SolveQuadratic(1, -2, 2);
Console.WriteLine("The
roots are {0} and {1}", ShowComplex(roots.Item1),
ShowComplex(roots.Item2));
Console.ReadKey();
}
static Tuple<Complex,
Complex> SolveQuadratic(double
a, double b, double
c)
{
if (a == 0)
throw new
ArgumentException("The coefficient of x
squared can't be zero");
double discriminant = b * b - 4.0 * a *
c;
Complex temp;
if (discriminant >= 0)
{
temp = new
Complex(Math.Sqrt(discriminant),
0);
}
else
{
temp = new
Complex(0, Math.Sqrt(-discriminant));
}
Complex root1 = (-b + temp) / (2.0 *
a);
Complex root2 = (-b - temp) / (2.0 *
a);
return Tuple.Create(root1,
root2);
}
static string
ShowComplex(Complex c)
{
if (c ==
Complex.Zero) return
"0";
if (c.Imaginary == 0.0)
return c.Real.ToString();
string imag;
if (c.Imaginary == 1)
imag = "i";
else if
(c.Imaginary == -1)
imag = "-i";
else
imag = c.Imaginary.ToString() + "i";
if (c.Real == 0.0)
return imag;
string sep = (c.Imaginary > 0.0) ?
"+" : "";
return c.Real.ToString() + sep + imag;
}
}
The output is now as expected:
The roots are 2i and -2i
The roots are 1+i and 1-i