Infine l'operazione di coniugio per un numero complesso
consiste nel lasciare inalterata la parte reale e prendere il
coefficiente opposto per la parte complessa di
:
Vale la seguente proprietà:
.
Il file di intestazione è il seguente:
// complesso.h // classe rappresentante un numero complesso #include <iostream.h> #include <math.h> class Complesso { public: // costruttore default Complesso (double x = 0, double y = 0) { re = x; im = y; } // le seguenti funzioni membro non sono necessarie // in quanto la classe non alloca memoria nello heap // Complesso (const Complesso&) // const Complesso& operator= (const Complesso&); // ~Complesso (); // funzioni di accesso (in lettura) double reale () const { return re; } double immaginaria () const { return im; } Complesso coniugio () const { return Complesso (re, -im); } void stampa () const; // operazioni sul numero complesso Complesso& coniuga () { im = -im; return *this; } double modulo () const { return sqrt(re*re + im*im); } double angolo () const { return asin (im / modulo()); } // operatori aritmetici Complesso operator+ (Complesso y) const { return Complesso (re + y.re, im + y.im); } Complesso operator- (Complesso y) const { return Complesso (re - y.re, im - y.im); } Complesso operator* (Complesso y) const; Complesso operator* (double y) const { return Complesso (re * y, im * y); } Complesso operator/ (Complesso y) const ; Complesso operator/ (double y) const { return Complesso (re / y, im /y); } // operatori aritmetici & assegnamento Complesso& operator+= (Complesso y); Complesso& operator-= (Complesso y); Complesso& operator*= (Complesso y); Complesso& operator*= (double y); Complesso& operator/= (Complesso y); Complesso& operator/= (double y); // operatori di confronto bool operator< (Complesso y) const; bool operator== (Complesso y) const; bool operator<= (Complesso y) const{ return (*this < y) || (*this == y); } bool operator!= (Complesso y) const { return !(*this == y); } bool operator> (Complesso y) const { return !(*this <= y); } bool operator>= (Complesso y) const{ return !(*this > y); } private: double re, im; // parte reale e immaginaria }; // overloading delle funzioni pow e sqrt della libreria math.h Complesso pow (Complesso base, double esponente); Complesso sqrt (Complesso x);
Il file contenente le definizioni delle funzioni non definite all'interno della dichiarazione di classe è il seguente:
// complesso.cpp #include "complesso.h" void Complesso::stampa () const { if (re == 0 && im == 0) { cout << "0"; return; } if (re != 0) cout << re; if (im < 0 && im != -1) cout << "-i" << fabs(im); else if (im > 0 && im != 1) cout << "+i" << im; else if (im == -1) cout << "-i"; else if (im == 1 && re != 0) cout << "+i"; else if (im == 1 && re == 0) cout << "i"; } Complesso Complesso::operator* (Complesso y) const { // creiamo un numero complesso temporaneo Complesso prodotto; prodotto.re = re * y.re - im * y.im; prodotto.im = re * y.im + im * y.re; return prodotto; } Complesso Complesso::operator/ (Complesso y) const { // creiamo un numero complesso temporaneo Complesso rapporto; // siccome la parte reale e quella immaginaria del // rapporto hanno il medesimo denominatore // allora lo creiamo una sola volta per entrambe double den = pow (y.modulo(), 2); rapporto.re = (re * y.re + im * y.im) / den; rapporto.im = (y.re * im - re * y.im) / den; return rapporto; } Complesso& Complesso::operator+= (Complesso y) { re += y.re; im += y.im; return *this; } Complesso& Complesso::operator-= (Complesso y) { re -= y.re; im -= y.im; return *this; } Complesso& Complesso::operator*= (Complesso y) { // creiamo un numero complesso temporaneo copia // di *this, il quale sara` infine restituito Complesso x(*this); re = x.re * y.re - x.im * y.im; im = x.re * y.im + x.im * y.re; return *this; } Complesso& Complesso::operator*= (double y) { re *= y; im *= y; return *this; } Complesso& Complesso::operator/= (Complesso y) { // creiamo un numero complesso temporaneo copia // di *this, il quale sara` infine restituito Complesso x(*this); double den = pow (y.modulo(), 2); re = (x.re * y.re + x.im * y.im) / den; im = (y.re * x.im - x.re * y.im) / den; return *this; } Complesso& Complesso::operator/= (double y) { re /= y; im /= y; return *this; } bool Complesso::operator< (Complesso y) const { if (modulo() < y.modulo()) return true; return false; } bool Complesso::operator== (Complesso y) const { if ( (re == y.re) && (im == y.im) ) return true; return false; } Complesso pow (Complesso base, double esponente) { double rho = base.modulo(); double theta = base.angolo(); rho = pow(rho, esponente); double alpha = cos(esponente * theta); double beta = sin(esponente * theta); return Complesso (rho * alpha, rho * beta); } Complesso sqrt (Complesso x) { return pow(x, .5); }
Molte funzioni sono state definite all'interno della dichiarazione della
classe, in quanto costituite da una o due righe, per motivi pratici (minore
tempo di programmazione della classe) e di efficienza; tralasceremo
volutamente precisazioni riguardo questi ultimi. Praticamente tutti gli
operatori aritmetici sono stati sovrapposti per consentire un utilizzo veloce
ed intuitivo della classe Complesso
, proprio come se si trattasse di un
tipo primitivo. Si noti come gli operatori di confronto siano stati definiti a
partire dall'operatore minore (<
) e uguale (==
); si tratta di un
procedimento comune, datosi che questi operatori godono di semplici
proprietà che li correlano l'un l'altro.
Oltre alla classe Complesso
, abbiamo definito la sovrapposizione di due
funzioni contenute nella math.h
: sqrt
, la quale calcola la
radice quadrata di un numero reale, e pow
, la quale eleva alla potenza
-esima un numero reale dato, adattandole ovviamente ai nostri scopi. La
prima di esse è stata definita utilizzando direttamente la seconda; nel caso
si abbia poca confidenza con i numeri complessi, si tralasci il procedimento
utilizzato all'interno di
pow
per effettuare l'elevamento a potenza. Si
verifichi comunque il funzionamento di tali funzioni passando loro per
argomento due numeri complessi aventi parte immaginaria nulla.
Un file di prova che mostri l'utilizzo della classe Complesso
è il
seguente:
// ex9_7_1.cpp #include <stdlib.h> #include <time.h> #include "complesso.h" void scambia (Complesso& a, Complesso& b) { Complesso t = a; a = b; b = t; } void ordina (Complesso v[], int n, bool crescente) { for (int i = 0; i < n; i++) for (int j = i; j < n; j++) if (crescente == true) { if (v[i] > v[j]) scambia (v[i], v[j]); } else { if (v[i] < v[j]) scambia (v[i], v[j]); } } void stampa (Complesso v[], int n) { for (int i = 0; i < n; i++) { cout << "\t"; v[i].stampa(); cout << "\tmodulo: " << v[i].modulo() << "\n"; } } void main() { srand ( time(0) ); Complesso x(4,3), y(3, -2), z(0,1), t; cout << "x: "; x.stampa(); cout << "\n"; cout << "y: "; y.stampa(); cout << "\n"; cout << "z: "; z.stampa(); cout << "\n"; cout << "|x|: "; cout << x.modulo() << "\n"; cout << "theta(x): "; cout << x.angolo() << "\n"; t = x + y; cout << "x + y: "; t.stampa(); cout << "\n"; t = x - y; cout << "x - y: "; t.stampa(); cout << "\n"; t = x * 1.5; cout << "x * 1.5: "; t.stampa(); cout << "\n"; t = x * z; cout << "x * z: "; t.stampa(); cout << "\n"; t = x / 2; cout << "x / 2: "; t.stampa(); cout << "\n"; t = x / z; cout << "x / z: "; t.stampa(); cout << "\n"; t = x.coniugio(); cout << "coniugio(x): "; t.stampa(); cout << "\n"; t = x.coniugio() * x; cout << "coniugio(x) * x: "; t.stampa(); cout << "\n"; t = pow (y, 3); cout << "pow(y,3): "; t.stampa(); cout << "\n"; t = sqrt (x); cout << "sqrt(x): "; t.stampa(); cout << "\n"; // genera un vettore di 5 complessi casuali // li ordina e li stampa Complesso v[5]; for (int i = 0; i < 5; i++) { double reale = rand() * 5.0 / double(RAND_MAX); double compl = rand() * 5.0 / double(RAND_MAX); v[i] = Complesso (reale, compl); } cout << "vettore di complessi casuali:\n"; stampa (v, 5); ordina (v, 5, true); cout << "vettore ordinato (crescente):\n"; stampa (v, 5); ordina (v, 5, false); cout << "vettore ordinato (decrescente):\n"; stampa (v, 5); }
esempio di output:
x: 4+i3
y: 3-i2
z: i
|x|: 5
theta(x): 0.643501
x + y: 7+i
x - y: 1+i5
x * 1.5: 6+i4.5
x * z: -3+i4
x / 2: 2+i1.5
x / z: 3-i4
coniugio(x): 4-i3
coniugio(x) * x: 25
pow(y,3): -9-i46
sqrt(x): 2.12132+i0.707107
vettore di complessi casuali:
2.7254+i1.19721 | modulo: 2.97676 | |
4.27832+i3.82083 | modulo: 5.7361 | |
3.62847+i3.24338 | modulo: 4.86676 | |
0.395223+i0.746467 | modulo: 0.844638 | |
3.2249+i1.93873 | modulo: 3.7628 |
0.395223+i0.746467 | modulo: 0.844638 | |
2.7254+i1.19721 | modulo: 2.97676 | |
3.2249+i1.93873 | modulo: 3.7628 | |
3.62847+i3.24338 | modulo: 4.86676 | |
4.27832+i3.82083 | modulo: 5.7361 |
4.27832+i3.82083 | modulo: 5.7361 | |
3.62847+i3.24338 | modulo: 4.86676 | |
3.2249+i1.93873 | modulo: 3.7628 | |
2.7254+i1.19721 | modulo: 2.97676 | |
0.395223+i0.746467 | modulo: 0.844638 |
Nell'esempio che precede non facciamo altro che mostrare la maggior parte
delle funzioni membro caratterizzanti un Complesso
. Si studino i
risultati ottenuti, confrontandoli con eventuali esempi scritti di proprio pugno.