next up previous contents index
Next: Numeri razionali Up: Due esempi concreti Previous: Due esempi concreti   Indice   Indice analitico

Numeri complessi

Per rappresentare un numero complesso utilizzeremo semplicemente due double, per la parte reale e quella immaginaria. Si ricorda che un qualunque numero complesso $x$ può essere scritto nella forma:

\begin{displaymath}
x = \alpha + i\beta
\end{displaymath}

ove $\alpha,\beta \in \mathcal{R}$, corrispondenti a $\alpha = \Re(x)$ e $\beta = \Im(x)$. Due forme alternative per rappresentare un numero complesso potrebbero essere:

\begin{eqnarray*}
x = \rho \cdot e^{i\theta} \\
x = \rho \left( \cos(\theta) + i\sin(\theta) \right)
\end{eqnarray*}



che noi non utilizzeremo. Ricordiamo inoltre che il modulo di un numero complesso $x$ è la distanza di esso dall'origine del piano di Gauss:

\begin{displaymath}
\rho = \vert x\vert = \sqrt{\alpha^2 + \beta^2}
\end{displaymath}

esso è pertanto un numero reale. Dati due numeri complessi $x = \alpha +
i\beta$ e $y = \alpha' + \beta'$ sono definite le seguenti operazioni:

\begin{eqnarray*}
& x + y = (\alpha + \alpha') + i(\beta + \beta') \\
& xy = \l...
...^2} + i
\frac{\alpha'\beta - \alpha\beta'}{\alpha'^2 + \beta'^2}
\end{eqnarray*}



Infine l'operazione di coniugio per un numero complesso $x = \alpha +
i\beta$ consiste nel lasciare inalterata la parte reale e prendere il coefficiente opposto per la parte complessa di $x$:

\begin{displaymath}
\bar{x} = \alpha - i\beta
\end{displaymath}

Vale la seguente proprietà: $x\bar{x} = \alpha^2 + \beta^2 = \vert x\vert^2$.

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 $x$-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

vettore ordinato (crescente):
 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

vettore ordinato (decrescente):
 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.


next up previous contents index
Next: Numeri razionali Up: Due esempi concreti Previous: Due esempi concreti   Indice   Indice analitico
Claudio Cicconetti
2000-09-06