LUNDS TEKNISKA HÖGSKOLA EDA011/017

LUNDS TEKNISKA HÖGSKOLA
Institutionen för datavetenskap
EDA011/017 Programmeringsteknik
2016/2017
Exempel från föreläsning 7
Jag demonstrerade två olika sätt att implementera en klass för komplexa tal. Även om Java inte
har inbyggda typer för komplexa tal (på samma sätt som int eller double) så kan vi skapa en
sådan klass själva. Idén är att vi då kan uttrycka komplexa beräkningar så här:
public class ComplexExample {
public static void main(String[] args) {
Complex a = new Complex(3, 4);
Complex b = new Complex(7, 9);
Complex c = a.plus(b);
System.out.println(c.toString());
System.out.println(c.getAbs());
}
}
Här skapas summan c av talen a = 3 + 4i och b = 7 + 9i. Vi förväntar oss en utskrift i stil
med
10.0+13.0i
16.401219466856727
Vi eftersträvar närmare bestämt följande specifikation:
Complex
/** Skapa ett komplext tal x + iy */
Complex(double x, double y);
/** Hämta realdelen */
double getRe();
/** Hämta imaginärdelen */
double getIm();
/** Hämta absolutbeloppet */
double getAbs();
/** Hämta argumentet (vinkeln) */
double getArg();
/** Hämta ett nytt komplext tal som motsvarar summan
av detta tal och other */
Complex plus(Complex other);
/* Hämta en sträng enligt mönstret 1.0+2.5i */
String toString();
1
2
Den första implementationen bygger på att vi använder real- och imaginärdel som attribut
(re och im) och därefter beräknar absolutbelopp och argument vid behov.
public class Complex {
private double re;
private double im;
/** Skapa ett komplext tal x + iy */
public Complex(double x, double y) {
this.re = x;
this.im = y;
}
// 1
/** Hämta realdelen */
public double getRe() {
return re;
}
/** Hämta imaginärdelen */
public double getIm() {
return im;
}
/** Hämta absolutbeloppet */
public double getAbs() {
return Math.sqrt(re * re + im * im);
}
/** Hämta argumentet (vinkeln) */
public double getArg() {
return Math.atan(im / re);
}
// 2
/** Hämta ett nytt komplext tal som motsvarar summan av detta tal och other */
public Complex plus(Complex other) {
return new Complex(this.getRe() + other.getRe(),
// 3
this.getIm() + other.getIm());
}
/* Hämta en sträng enligt mönstret 1.0+2.5i */
public String toString() {
return this.getRe() + "+" + this.getIm() + "i";
}
// 3
}
Kommentarer:
1. Här skulle man lika gärna kunna skriva re = x . Att skriva this framför är inte nödvändigt
eftersom det inte finns något annat som heter re här. I andra fall har vi ofta haft parametrar
som heter samma sak som attributen, och då är this nödvändigt.
2. Som studenten D mycket riktigt påpekade fungerar arctan-beräkningen bara i första kvadranten. Det har emellertid inte så mycket med Java-klasser att göra som med föreläsarens
pinsamt dimmiga minnen av polär form för komplexa tal.
3. Jag har skrivit this.getRe() istället för this.re. Det spelar ingen roll här, men i den andra
implementationen är det praktiskt, som vi strax ska se.
I den andra implementationen av samma klass valde jag istället att ha absolutbelopp och
argument (vinkel) som attribut, och beräknar real- och imaginärdel vid behov.
3
public class Complex {
private double r;
private double phi;
/** Skapa ett komplext tal x + iy */
public Complex(double x, double y) {
this.r = Math.sqrt(x * x + y * y);
this.phi = Math.atan(y / x);
}
/** Hämta realdelen */
public double getRe() {
return r * Math.cos(phi);
}
/** Hämta imaginärdelen */
public double getIm() {
return r * Math.sin(phi);
}
/** Hämta absolutbeloppet */
public double getAbs() {
return r;
}
/** Hämta argumentet (vinkeln) */
public double getArg() {
return phi;
}
/** Hämta ett nytt komplext tal som motsvarar summan av detta tal och other */
public Complex plus(Complex other) {
return new Complex(this.getRe() + other.getRe(),
this.getIm() + other.getIm());
}
/* Hämta en sträng enligt mönstret 1.0+2.5i */
public String toString() {
return this.getRe() + "+" + this.getIm() + "i";
}
}
Huvudprogrammet ovan fungerar likadant oavsett hur vi väljer våra attribut. Däremot blir
det en liten, liten numerisk skillnad (2 · 10−15 ) när man kör det:
10.0+13.000000000000002i
16.401219466856727
Skillnaden beror på att värdet x + yi räknas om från rektangulär till polär form när vi skapar
Complex-objekt i den andra lösningen, och därefter åter till rektangulär form i additionen. I
summan hanteras c i polär form, och därefter räknas realdelen än en gång ut när toString
anropar getIm. I den första lösningen var imaginärdelen ett attribut och någon konvertering var
alltså inte nödvändig, men här ger den begränsade precisionen i double små avrundningsfel.
Om man vill undvika sådana avrundningsfel för just imaginär- och realdelen, så är den första
lösningen bättre här. Om man däremot ofta vill räkna ut absolutbelopp och argument är den
andra något effektivare. Även om detta exempel är väldigt enkelt illustrerar det hur man ibland
kan välja attribut beroende på vilka operationer som måste vara särskilt effektiva.
Det viktiga med detta exempel är att illustrera att samma klass ofta kan implementeras
på olika sätt, med olika attribut. Diskussionen om beräkningsprecision och effektivitet ovan är
väldigt ytlig, och är mindre viktig här.