Los números complejos

chapter.gif (1105 bytes)home.gif (1232 bytes)next.gif (998 bytes)

Definición de número complejo

Los constructores

Mostrar un número complejo

El número complejo en forma polar

Operaciones con números complejos

El código fuente


El estudio de la clase Fraccion nos proporciona una pista para abordar el estudio de otras clases que representan a entidades matemáticas como la clase Complejo.

En esta página crearemos una clase denominada Complejo, y definiremos las operaciones entre números complejos: suma, producto y cociente de dos números, el producto y cociente de un número complejo por un número real, la potencia de un número complejo, que se utilizará para calcular el valor numérico de una función polinomómica cuando su variable x es un número complejo.

Otras cuestiones que trataremos, serán la representación polar de un número complejo, el conjugado de un número complejo, y la forma en la que podemos mostrar un número complejo redefiniendo la función miembro toString de la clase base Object.

 

Definición de número complejo

Un número complejo, es una entidad matemática que viene dada por un par de números reales, el primero x se denomina la parte real y al segundo y la parte imaginaria. Los números complejos se representa por un par de números entre paréntesis (x, y), como los puntos del plano, o bien, en la forma usual de x+yi, i se denomina la unidad imaginaria, la raíz cuadrada de menos uno. La clase Complejo constará de dos miembros dato, la parte real real, y la parte imaginaria imag, ambos del tipo predefinido double.

public class Complejo{
     private double real;
     private double imag;
//faltan las funciones miembro
}

 

Los constructores

Crearemos los objetos de la clase Complejo, que denominaremos números complejos, o simplemente complejos, llamando a algunos de los constructores de dicha clase. Tendremos un constructor por defecto, que reserva espacio en memoria para dos números del tipo double, y los inicializa por defecto, a cero. El constructor por defecto se llama cuando se crea un objeto c de la clase Complejo de la forma

	Complejo c=new Complejo();

El constructor explícito, reserva espacio en memoria, e inicializa los miembros dato con los valores que el usuario le pasa al constructor. Para crear un número complejo cuya parte real sea 2 y cuya parte imaginaria sea 3, se escribe.

	Complejo c=new Complejo(2.0, 3.0);

La definición de la clase Complejo con dos constructores será la siguiente

public class Complejo{
     private double real;
     private double imag;

  public Complejo() {
     real=0.0;
     imag=0.0;
  }
  public Complejo(double real, double imag){
     this.real=real;
     this.imag=imag;
  }
//otras funciones miembro
}

 

Mostrar un número complejo

Para mostrar un número complejo transformándolo en su representación textual, se redefine la función miembro toString de la clase base Object. En el lenguaje Java todas las clases derivan de la clase base Object, que es una clase que tiene una implementación mínima.

public class Complejo{
     private double real;
     private double imag;

    public String toString(){
	//...
    }
}

Como vemos los miembros dato de la clase Complejo tienen el modificador private, por tanto no son accesibles fuera de dicha clase, pero podemos conocer los valores que guardan a través de la redefinición de toString.

La función toString devuelve un string es decir, un objeto de la clase String, que se llama implícitamente cuando se pone un objeto de la clase Complejo como el argumento de la función println.

	Complejo c=new Complejo(2.0, 3.0);
	System.out.println(c);

Podemos redefinir toString como queramos, por ejemplo, que la parte real e imaginaria aparezcan entre paréntesis separados por una coma, o bien, de la forma habitual x+yi. Además, limitamos el número de decimales que aparecen en la pantalla a dos, empleando la función estática Math.round. Hay que tener en cuanta que esta función devuelve un entero int que hemos de promocionar (casting) a double para poder efectuar la división entre 100.

	(double)Math.round(100*real)/100

Por defecto, los números positivos no muestran su signo delante. Por tanto, si y fuese positivo el número complejo se mostraría como x yi. Para para mostrar adecuadamente el número complejo cuando la parte imaginaria es positiva y cuando es negativa, se ha dividido el código de la función toString en dos partes

  public String toString(){
     if(imag>0) return new String((double)Math.round(100*real)/100+
		"  + "+(double)Math.round(100*imag)/100+"*i");
     return new String((double)Math.round(100*real)/100+
		" - "+(double)Math.round(-100*imag)/100+"*i");
  }

 

El número complejo en forma polar

También, podemos conocer el número complejo en el formato denominado polar. Basta conocer un poco de geometría para transformar un complejo en formato binario (x, y) a formato polar rq, tal como puede verse en la figura. x e y son respectivamente, la abscisa y ordenada del punto, r es la distancia desde el origen al punto, y q es el ángulo que forma con el eje X. El código de las funciones denominadas modulo y argumento es el siguiente.

FIG12_07.gif (2080 bytes)
  
  public double modulo(){
     return Math.sqrt(real*real+imag*imag);
  }
  public double argumento(){
     double angulo=Math.atan2(imag, real);    
     if(angulo<0)  angulo=2*Math.PI+angulo;
     return angulo*180/Math.PI;
  }

Para hallar el argumento o ángulo q  que forma con el eje X, se emplea la función estática Math.atan2, que devuelve el ángulo en radianes entre -p y +p. Dicho ángulo lo hemos de transformar en un ángulo comprendido entre 0 y 2p y después expresarlo en grados.

 

Operaciones con números complejos

Declararemos las funciones que realizan las operaciones entre números complejos como estáticas, ligadas a la clase Complejo en vez de a objetos de dicha clase, siguiendo el esquema trazado para la clase Fraccion.

 

Suma de dos números complejos

Cuando se suman dos números complejos la parte real es la suma de las partes reales de los complejos sumandos, y la parte imaginaria, es la suma de las partes imaginarias de los sumandos. La definición de la función suma no reviste dificultad alguna, ya que devuelve un complejo cuya parte real es la suma de la parte real de c1 y la parte real c2, y cuya parte imaginaria es la suma de la parte imaginaria de c1 y la parte imaginaria de c2

  public static Complejo suma(Complejo c1, Complejo c2){
     double x=c1.real+c2.real;
     double y=c1.imag+c2.imag;
     return new Complejo(x, y);
  }

Para sumar dos números complejos se efectúa la llamada a la función estática suma de la siguiente forma

        Complejo a1=new Complejo(1.0, 2.0);
        Complejo a2=new Complejo(-1.0, 3.0); 
        Complejo resultado=Complejo.suma(a1, a2);
        System.out.println("Suma "+resultado);

 

Producto de dos números complejos

La regla es ahora un poco más compleja, pero la codificación de la función producto es similar a la de la función suma

 public static Complejo producto(Complejo c1, Complejo c2){
     double x=c1.real*c2.real-c1.imag*c2.imag;
     double y=c1.real*c2.imag+c1.imag*c2.real;
     return new Complejo(x, y);
  }

Como variantes de esta operación tenemos el producto de un número real por un número complejo, y la operación commutativa, el producto de un número complejo por un número real. Cuando se multiplica un número real por un número complejo se multiplica su parte real y su parte imaginaria por dicho número real.

  public static Complejo producto(Complejo c, double d){
     double x=c.real*d;
     double y=c.imag*d;
     return new Complejo(x, y);
 }
  public static Complejo producto(double d, Complejo c){
     double x=c.real*d;
     double y=c.imag*d;
     return new Complejo(x, y);
 }

Las tres funciones producto tienen el mismo nombre. El lenguaje Java permite sobrecargar las funciones miembro siempre que tengan distinto número de parámetros, o el mismo número de parámetros pero de distinto tipo.

Podemos ahorrarnos las variables locales x e y, definiendo las funciones producto del siguiente modo.

  public static Complejo producto(Complejo c, double d){
     return new Complejo(c.real*d, c.imag*d);
 }
  public static Complejo producto(double d, Complejo c){
     return new Complejo(c.real*d, c.imag*d);
 }

Para multiplicar dos números complejos se efectúa la llamada a la función estática producto de la siguiente forma

        Complejo a1=new Complejo(1.0, 2.0);
        Complejo a2=new Complejo(-1.0, 3.0); 
        Complejo resultado=Complejo.producto(a1, a2);
        System.out.println("Producto "+resultado);
        resultado=Complejo.producto(a1, 3.5);
        System.out.println("Producto "+resultado);
        resultado=Complejo.producto(2.0, a2);
        System.out.println("Producto "+resultado);

 

Cociente de dos números complejos

La fórmula para hallar el cociente de dos números complejos es

El número complejo c-di se dice que es el conjugado de c+di. El producto de un complejo por su conjugado nos da el cuadrado de su módulo. Si el módulo del número complejo denominador es cero, entonces se lanza una excepción que notifica al usuario de esta evantualidad. La forma en la que se lanza (throw) una excepción aparece en el código.

  public static Complejo cociente(Complejo c1, Complejo c2) throws ExcepcionDivideCero{
     double aux, x, y;
     if(c2.modulo()==0.0){
          throw new ExcepcionDivideCero("Divide entre cero");
     }else{
          aux=c2.real*c2.real+c2.imag*c2.imag;
          x=(c1.real*c2.real+c1.imag*c2.imag)/aux;
          y=(c1.imag*c2.real-c1.real*c2.imag)/aux;
     }
     return new Complejo(x, y);
  }

Cuando el módulo del complejo denominador es cero, se crea un objeto de la clase ExcepcionDivideCero derivada de la clase base Exception. La definición de esta clase es muy simple, ya que se limita a pasar el mensaje "Divide entre cero" al constructor de la clase base.

class ExcepcionDivideCero extends Exception {
  public  ExcepcionDivideCero() {
         super();
  }
  public ExcepcionDivideCero(String s) {
         super(s);
  }
}

Cuando se efectúe el cociente entre dos números complejos se deberá colocar la llamada a la operación cociente en un bloque try...catch.

        Complejo a1=new Complejo(1.0, 2.0);
        Complejo a2=new Complejo(-1.0, 3.0); 
	Complejo resultado;
	try{
       	    resultado=Complejo.cociente(a1, a2);
        }catch(ExcepcionDivideCero ex){
            System.out.println("Al calcular el cociente se ha producido una excepción\n "
                +ex.getClass()+ " con el mensaje "+ ex.getMessage());
        }
        System.out.println("Cociente "+resultado);

El lector podría pensar que no es necesario definir la excepción propia ExcepcionDivideCero cuando el lenguaje Java dispone de ArithmeticException para este propósito. Sin embargo, esta excepción se lanza solamente cuando se dividen números enteros.

 

Potencia de un número complejo

Para hallar la potencia de un número complejo, aplicamos el binomio de Newton. Teniendo en cuenta las potencias de la unidad imaginaria el resultado es

otro número complejo cuya parte real es

(1)

y cuya parte imaginaria es

(2)

Precisamos, para llevar a cabo este cálculo de dos funciones auxiliares denominadas combinatorio y potencia, una que calcule en número combinatorio m sobre n, y otra que calcule el resultado de elevar un número real a una potencia entera.

Empezaremos por la más sencilla, la que calcula la potencia de un número real que será similar a la potencia de un número entero que ya estudiamos. Para este propósito, se puede emplear la función Math.pow, que tiene dos parámetros del tipo double.

  private static double potencia(double base, int exponente){
    double resultado=1.0;
    for(int i=0; i<exponente; i++){
        resultado*=base;
    }
    return resultado;
  }

Ya se calculó el factorial de un número, y el número combinatorio m sobre n, en términos de la función factorial.

Alternativamente, podemos definir una función combinatorio que devuelva el cociente entre el numerador y el denominador

  private static double combinatorio(int m, int n){
    long num=1;
    long den=1;
    for(int i=m; i>m-n; i--){
        num*=i;
    }
    for(int i=2; i<=n; i++){
        den*=i;
    }
    return (double)num/den;
  }

El primer bucle for calcula el numerador, y el segundo bucle for halla el denominador. Ya que se calculan productos de números enteros, el resultado se guarda por precaución en un número de tipo long, para evitar que se sobrepase el valor del mayor entero que se puede guardar en el tipo predefinido int, lo que no ocurrirá casi nunca a efectos prácticos.

Para obtener el cociente se ha de promocionar (casting) el numerador de long a double.

    return (double)num/den;

Finalmente, definimos la potencia de un número complejo en términos de las dos funciones auxiliares anteriores. Se traduce la expresión (1) a código para hallar la parte real del número complejo resultante, y la expresión (2) para hallar su parte imaginaria.

Nos daremos cuenta que el número total de términos del desarrollo del binomio es par, si el exponente es impar, y es impar si el exponente es par. En el primer caso, el número de términos que contribuyen a la parte real es igual a los que contribuyen a la parte imaginaria. En el segundo caso, tendremos un término menos que contribuye a la parte imaginaria, por este motivo se ha de salir del bucle (sentencia break) después de calcular la última contribución a la parte real. Podemos observar, que los signos de los distintos términos son alternados, si el índice del término es par el signo es positivo, y si es impar el signo es negativo, esto lo podemos calcular mediante la versión simplificada de if... else.

        signo=(i%2==0) ? +1 : -1;

El código completo de la función potencia es, el siguiente

 public static Complejo potencia(Complejo c, int exponente){
    double x=0.0, y=0.0;
    int signo;
    for(int i=0; i<=exponente; i++){
        signo=(i%2==0) ? 1 : -1;
//parte real
        x+=combinatorio(exponente, 2*i)*potencia(c.real, exponente-2*i)*potencia(c.imag, 2*i)*signo;
        if(exponente==2*i)  break;
//parte imaginaria  
        y+=combinatorio(exponente, 2*i+1)*potencia(c.real, exponente-(2*i+1))*potencia(c.imag, 2*i+1)*signo;
    }
    return new Complejo(x, y);
  }

Para efectuar la llamada a la función potencia, por ejemplo, para hallar el cubo de un número complejo

        Complejo a1=new Complejo(1.0, 2.0);
        Complejo resultado=Complejo.potencia(a1, 3);
        System.out.println("Potencia "+resultado);

La función potencia se utiliza para calcular el valor numérico de una función polinomómica cuando su variable x es un número complejo.

 

El código fuente

disco.gif (1035 bytes)Complejo.java, ComplejoApp.java