Komplexe Zahlen: Die Klasse Complex

1. Aufgabe

Die Theorie der komplexen Zahlen ist auf den Mathematiker Carl Friedrich Gauß zurückzuführen, der diese erfand, um beispielsweise Gleichungen der Gestalt

(1)                  x2 = -1

lösen zu können. Mit Hilfe einer genialen Idee, der Erfindung der imaginären Zahlen, lassen sich Gleichungen lösen, für die im Bereich der reellen Zahlen keine Lösung existiert. Der „Urvater“ aller imaginären Zahlen ist die Zahl i mit der Eigenschaft

i * i = -1.

Die Gleichung (1) ist nun mit Hilfe von i lösbar, es sind x1 = i und x2 = -i die beiden Lösungen. Alle imaginären Zahlen sind Vielfache der Zahl i und werden auch so geschrieben, zum Beispiel 3i = 3 * i oder 0.99999i = 0.99999 * i. Komplexe Zahlen sind nun Zahlen, die sich aus einer reellen und einer imaginären Zahl zusammensetzen. Man spricht vom Real- und Imaginärteil einer komplexen Zahl und stellt sie in der Form

z = x + iy

dar, wobei x den realen und y den durch das i gekennzeichneten imaginären Anteil darstellen. Beispiele komplexer Zahlen sind 1 + i2 oder 1.11111 + i2.22222 oder aber auch nur i6 (sprich diese komplexe Zahl besitzt keinen Realteil) oder aber 5.5 (komplexe Zahl, deren imaginärer Anteil gleich Null ist). Die reellen Zahlen werden als Teilmenge der komplexen Zahlen aufgefasst. Für die grafische Darstellung komplexer Zahlen hat Gauß die nach ihm benannte komplexe (Gaußsche) Zahlenebene eingeführt, siehe Abbildung 1. Die Punkte der x-Achse eines normalen, kartesischen Koordinatensystems, der reellen Achse, entsprechen den reellen, die Punkte der y-Achse, der imaginären Achse, den rein imaginären unter den komplexen Zahlen.

Jede komplexe Zahl entspricht einem Punkt in der Ebene.

Abbildung 1. Jede komplexe Zahl entspricht einem Punkt in der Ebene.


In Abbildung 1 erkennen Sie die Darstellung der komplexen Zahl 4 + i2 durch einen entsprechenden Ortsvektor in der komplexen Zahlenebene. Für das Rechnen mit komplexen Zahlen hat man die Grundrechenarten +, -, * und / definiert, sie lauten für zwei komplexe Zahlen z1 = x1 + iy1 und z2 = x2 + iy2 wie folgt:

Addition:

z1 + z2 =

(x1 + iy1) + (x2 + iy2) = (x1 + x2) + i(y1 + y2)

Subtraktion:

z1 - z2 =

(x1 + iy1) - (x2 + iy2) = (x1 - x2) + i(y1 - y2)

Multiplikation:

z1 * z2 =

(x1 + iy1) * (x2 + iy2) = x1x2 + ix1y2 + ix2y1 - y1y2 =

 

 

x1x2 - y1y2 + i(x1y2 + x2y1)

Division:

z1 / z2 =

(x1 + iy1) / (x2 + iy2) =

 

 

(x1x2 + y1y2) / (x22 + y22) + i(x2y1 - x1y2) / (x22 + y22)

Tabelle 1. Die Grundrechenarten komplexer Zahlen.


Neben diesen elementaren Rechenoperationen gibt es noch den Betrag einer komplexen Zahl. Darunter verstehen wir in der komplexen Zahlenebene den Abstand einer komplexen Zahl zum Ursprung. Nach dem Satz von Pythagoras gilt für das Betragsquadrat einer komplexen Zahl z = x + iy

|z|2 = x2 + y2.

Mit der konjugiert komplexen Zahl schließen wir diesen Ausflug in die Theorie komplexer Zahlen ab, in Bezug auf eine komplexe Zahl z = x + iy besitzt sie den Wert x - iy.

Erstellen Sie eine Klasse Complex unter Berücksichtigung von Tabelle 2:

Element

Beschreibung

Konstruktoren

Complex ();
Complex (double, double);
Complex (double);

Standard- und benutzerdefinierter Konstruktor. Der dritte Konstruktor ist ein Typkonvertierungskonstruktor.

Arithmetische Operatoren

friend Complex operator+ (const Complex&, const Complex&);
friend Complex operator- (const Complex&, const Complex&);
friend Complex operator* (const Complex&, const Complex&);
friend Complex operator/ (const Complex&, const Complex&);

Addition, Subtraktion, Multiplikation und Division zweier komplexer Zahlen.

Negation

friend Complex operator- (const Complex&);

Negation einer komplexen Zahl (unäres Minus).

Vergleichsoperatoren

friend bool operator== (const Complex&, const Complex&);
friend bool operator!= (const Complex&, const Complex&);

Vergleich zweier komplexer Zahlen auf Gleichheit oder Ungleichheit.

setter- und getter-Methoden

void SetReal (double);
double GetReal ();
void SetImag (double);
double GetImag ();

Lesender und schreibender Zugriff auf den Real- und Imaginärteil einer komplexen Zahl.

Abs

double Abs();

Betrag einer komplexen Zahl.

operator!

friend Complex operator! (const Complex&);

Liefert die konjugierte komplexe Zahl zurück.

Ein- und Ausgabe

friend ostream& operator<< (ostream&, const Complex&);
friend istream& operator>> (istream&, Complex&);

Gibt eine komplexe Zahl auf der Konsole im vektoriellen Format [x,y] aus bzw. liest eine komplexe Zahl von der Konsole ein.

Tabelle 2. Konstruktoren, Methoden und Operatoren der Klasse Complex.


Zum Testen Ihrer Klasse finden Sie Anregungen im nachfolgenden Testrahmen vor:

01: #include <iostream>
02: using namespace std;
03: 
04: #include "Complex.h"
05: 
06: void main ()
07: {
08:     // testing c'tors
09:     Complex c1 = Complex ();
10:     Complex c2 = Complex (3.0, 4.0);
11:     cout << "c1: " << c1 << endl;
12:     cout << "c2: " << c2 << endl;
13: 
14:     // testing input operator
15:     cout << "enter complex number (e.g. \"1.5 3.5\"):" << endl;
16:     cin >> c1;
17:     cout << "c1: " << c1 << endl;
18: 
19:     // testing setter and getter methods
20:     c2.SetReal (5);
21:     cout << "c2: " << c2 << endl;
22:     double imag = c2.GetImag ();
23:     cout << "imaginary part of c2: " << imag << endl;
24: 
25:     // testing unary arithmetic operators
26:     Complex c3 = -c1;
27:     cout << "-c1: " << c3 << endl;
28: 
29:     // testing binary arithmetic operators
30:     c3 = c1 + c2;
31:     cout << "c1 + c2: " << c3 << endl;
32:     c3 = c1 - c2;
33:     cout << "c1 - c2: " << c3 << endl;
34:     c3 = c1 * c2;
35:     cout << "c1 * c2: " << c3 << endl;
36:     c3 = c1 / c2;
37:     cout << "c1 / c2: " << c3 << endl;
38: 
39:     // testing conversion constructor
40:     c3 = c1 + 5.0;
41:     cout << "c1 + 5.0: " << c3 << endl;
42:     c3 = 5 + c1;
43:     cout << "5 + c1: " << c3 << endl;
44:     c3 = 10.0;
45:     cout << "c3 = 10.0: " << c3 << endl;
46: 
47:     // testing conjunction operator
48:     cout << "c1: " << c1 << endl;
49:     c3 = !c1;
50:     cout << "!c1: " << c3 << endl;
51: 
52:     // testing copy c'tor
53:     Complex c4 (c1);
54:     cout << "c4: " << c4 << endl;
55: 
56:     // testing assignment operator
57:     c4 = c3;
58:     cout << "c4: " << c4 << endl;
59: }

Ausgabe:

c1: [0+0*i]
c2: [3+4*i]
enter complex number (e.g. "1.5 3.5"):
2.6 3.1
c1: [2.6+3.1*i]
c2: [5+4*i]
imaginary part of c2: 4
-c1: [-2.6-3.1*i]
c1 + c2: [7.6+7.1*i]
c1 - c2: [-2.4-0.9*i]
c1 * c2: [0.6+25.9*i]
c1 / c2: [0.9918+0.0234283*i]
c1 + 5.0: [7.6+3.1*i]
5 + c1: [7.6+3.1*i]
c3 = 10.0: [10+0*i]
c1: [2.6+3.1*i]
!c1: [2.6-3.1*i]
c4: [2.6+3.1*i]
c4: [2.6-3.1*i]

2. Lösung

Quellcode: Siehe auch https://github.com/peterloos

Die Lösung dieser Aufgabe entnehmen Sie bitte Listing 1 (Schnittstelle) und Listing 2 (Implementierung):

01: class Complex
02: {
03: private:
04:     double m_real;
05:     double m_imag;
06: 
07: public:
08:     // c'tors
09:     Complex ();
10:     Complex (double, double);
11: 
12:     // conversion constructor
13:     Complex (double);
14: 
15:     // setter / getter methods
16:     void SetReal (double real) { m_real = real; }
17:     double GetReal () { return m_real; }
18:     void SetImag (double imag) { m_imag = imag; }
19:     double GetImag () { return m_imag; }
20: 
21:     // unary arithmetic operators
22:     friend Complex operator- (const Complex&);
23: 
24:     // binary arithmetic operators
25:     friend Complex operator+ (const Complex&, const Complex&);
26:     friend Complex operator- (const Complex&, const Complex&);
27:     friend Complex operator* (const Complex&, const Complex&);
28:     friend Complex operator/ (const Complex&, const Complex&);
29: 
30:     // comparison operators
31:     friend bool operator== (const Complex&, const Complex&);
32:     friend bool operator!= (const Complex&, const Complex&);
33: 
34:     // complex conjunction operator
35:     friend Complex operator! (const Complex&);
36: 
37:     // input / output operators
38:     friend ostream& operator<< (ostream&, const Complex&);
39:     friend istream& operator>> (istream&, Complex&);
40: };

Beispiel 1. Klasse Complex: Schnittstelle.


001: #include <iostream>
002: using namespace std;
003: 
004: #include "Complex.h"
005: 
006: // c'tors
007: Complex::Complex ()
008: {
009:     m_real = 0.0;
010:     m_imag = 0.0;
011: }
012: 
013: Complex::Complex (double real, double imag)
014: {
015:     m_real = real;
016:     m_imag = imag;
017: }
018: 
019: // conversion constructor
020: Complex::Complex (double real)
021: {
022:     m_real = real;
023:     m_imag = 0.0;
024: }
025: 
026: // unary arithmetic operators
027: Complex operator- (const Complex& c)
028: {
029:     return Complex (-c.m_real, -c.m_imag);
030: }
031: 
032: // binary arithmetic operators
033: Complex operator+ (const Complex& c1, const Complex& c2)
034: {
035:     Complex tmp = Complex (c1);
036:     tmp.m_real += c2.m_real;
037:     tmp.m_imag += c2.m_imag;
038:     return tmp;
039: }
040: 
041: Complex operator- (const Complex& c1, const Complex& c2)
042: {
043:     Complex tmp = Complex (c1);
044:     tmp.m_real -= c2.m_real;
045:     tmp.m_imag -= c2.m_imag;
046:     return tmp;
047: }
048: 
049: Complex operator* (const Complex& c1, const Complex& c2)
050: {
051:     double real = c1.m_real * c2.m_real - c1.m_imag * c2.m_imag;
052:     double imag = c1.m_real * c2.m_imag + c1.m_imag * c2.m_real;
053:     return Complex (real, imag);
054: }
055: 
056: Complex operator/ (const Complex& c1, const Complex& c2)
057: {
058:     double real =
059:         (c2.m_real * c1.m_real + c2.m_imag * c1.m_imag) /
060:         (c2.m_imag * c2.m_imag + c1.m_imag * c1.m_imag);
061: 
062:     double imag =
063:         (c2.m_real * c1.m_real - c2.m_imag * c1.m_imag) /
064:         (c2.m_imag * c2.m_imag + c1.m_imag * c1.m_imag);
065: 
066:     return Complex (real, imag);
067: }
068: 
069: // comparison operators
070: bool operator== (const Complex& c1, const Complex& c2)
071: {
072:     if (c1.m_real == c2.m_real && c1.m_imag == c2.m_imag)
073:         return true;
074: 
075:     return false;
076: }
077: 
078: bool operator!= (const Complex& c1, const Complex& c2)
079: {
080:     return !(c1 == c2);
081: }
082: 
083: // complex conjunction operator
084: Complex operator! (const Complex& c)
085: {
086:     return Complex (c.m_real, - c.m_imag);
087: }
088: 
089: // output operator
090: ostream& operator<< (ostream& os, const Complex& c)
091: {
092:     char* sign = (c.m_imag >= 0) ? "+" : "";
093:     os << "[" << c.m_real << sign << c.m_imag << "*i]";
094:     return os;
095: }
096: 
097: istream& operator>> (istream& is, Complex& c)
098: {
099:     is >> c.m_real >> c.m_imag;
100:     return is;
101: }

Beispiel 2. Klasse Complex: Implementierung.