Komplexe Zahlen in JavaScript
Einführung
Ich habe in letzter Zeit mit der Mandelbrot-Menge gespielt, was dazu geführt hat, dass ich meine Erinnerung daran, wie man mit komplexen Zahlen rechnet, abstauben musste. Ich habe eine nette JavaScript-Klassenhilfe erstellt, die sie verwendet. Hier ist jetzt, es funktioniert (es ist eine ziemlich trockene Erklärung, aber ich hoffe, es ist nützlich).
Eine schnelle Auffrischung
Wenn wir zwei Zahlen miteinander multiplizieren, ist das Ergebnis ungerade oder gerade , je nachdem, ob die Zahlen, die wir multiplizieren, lauten:
-1 * 1 = -1
1 * -1 = -1
1 * 1 = 1
-1 * -1 = 1
i ≔ √(-1)
∴ i² = -1
Eine komplexe Zahl ist eine Zahl, die einen Realteil und einen Imaginärteil hat . Sie sind in der Form geschrieben a + bi.
Da 0i = 0sich die Zahlenlinien bei treffen 0, können wir diese Zahlen in einem Diagramm darstellen, wobei die x-Achse normalerweise die reelle Zahlenlinie und die y-Achse die imaginäre ist.
Einige Beispiele
Arithmetik
Sie haben vielleicht bemerkt, dass komplexe Zahlen sehr wie Vektoren aussehen, und mit dem Vorbehalt, dass i * i = -1sie es sind. Alle Operationen sind nur reguläre Vektoroperationen, die basierend auf dieser Einschränkung angepasst werden.
Code
Der ComplexKlassenkonstruktor hat zwei Parameter.
realimaginary
export class Complex {
constructor(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
}
}
/***
* Generate a new <code>Complex(0,0)</code>
* @returns {Complex}
*/
static zero = () => new Complex(0, 0);
/***
* Returns a string in the form <code>a ± bi</code>.
* @returns {string}
*/
toString() {
const operator = this.imaginary < 0 ? '-' : '+';
return `${this.real} ${operator} ${Math.abs(this.imaginary)}i`;
}
/***
* Both <i>real</i> and <i>imaginary</i> parts are equal.
* @param other
* @returns {boolean}
*/
equals(other) {
return this.real === other.real && this.imaginary === other.imaginary;
}
add(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
);
}
subtract(other) {
return new Complex(
this.real — other.real,
this.imaginary — other.imaginary
);
}
/***
* Multiple this Complex with another.<br/>
* <code>(a + bi)(c + di) = (ac - bd) + (ad + bc)i</code>
* @param other
* @returns {Complex}
*/
multiply(other) {
return new Complex(
this.real * other.real - this.imaginary * other.imaginary,
this.real * other.imaginary + this.imaginary * other.real
);
}
/***
* <code>(a + bi) / (c + di) = [(ac + bd) / (c^2 + d^2)] + [(bc - ad) / (c^2 + d^2)]i<code>
* @param other
*/
divide(other) {
const otherMagnitudeSquared =
other.real * other.real + other.imaginary * other.imaginary;
const r =
(this.real * other.real + this.imaginary * other.imaginary) /
otherMagnitudeSquared;
const i =
(this.imaginary * other.real - this.real * other.imaginary) /
otherMagnitudeSquared;
return new Complex(r, i);
}
Anwendungsklasse
export class Complex {
constructor(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
}
/***
* Generate a new <code>Complex(0,0)</code>
* @returns {Complex}
*/
static zero = () => new Complex(0, 0);
add(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
);
}
subtract(other) {
return new Complex(
this.real - other.real,
this.imaginary - other.imaginary
);
}
/***
* Multiple this Complex with another.<br/>
* <code>(a + bi)(c + di) = (ac - bd) + (ad + bc)i</code>
* @param other
* @returns {Complex}
*/
multiply(other) {
return new Complex(
this.real * other.real - this.imaginary * other.imaginary,
this.real * other.imaginary + this.imaginary * other.real
);
}
/***
* <code>(a + bi) / (c + di) = [(ac + bd) / (c^2 + d^2)] + [(bc - ad) / (c^2 + d^2)]i<code>
* @param other
*/
divide(other) {
const otherMagnitudeSquared =
other.real * other.real + other.imaginary * other.imaginary;
const r =
(this.real * other.real + this.imaginary * other.imaginary) /
otherMagnitudeSquared;
const i =
(this.imaginary * other.real - this.real * other.imaginary) /
otherMagnitudeSquared;
return new Complex(r, i);
}
magnitude() {
return Math.sqrt(
this.real * this.real + this.imaginary * this.imaginary
);
}
/***
* Returns a string in the form <code>a ± bi</code>.
* @returns {string}
*/
toString() {
const operator = this.imaginary < 0 ? '-' : '+';
return `${this.real} ${operator} ${Math.abs(this.imaginary)}i`;
}
/***
* Both <i>real</i> and <i>imaginary</i> parts are equal.
* @param other
* @returns {boolean}
*/
equals(other) {
return this.real === other.real && this.imaginary === other.imaginary;
}
}
Hier kommt Vitest zum Einsatz.
import { describe, it, expect } from 'vitest';
import { Complex } from './Complex.js';
describe('Calling zero()', () => {
const zero = Complex.zero();
it('should return 0 real part', () => {
expect(zero.real).toBe(0);
});
it('should return 0 imaginary part', () => {
expect(zero.imaginary).toBe(0);
});
});
describe('Creating a new number', () => {
const expectedReal = Math.random();
const expectedImaginary = Math.random();
const actual = new Complex(expectedReal, expectedImaginary);
it(`should return the expected real part (${expectedReal})`, () => {
expect(actual.real).toBe(expectedReal);
});
it(`should return the expected imaginary part (${expectedImaginary})`, () => {
expect(actual.imaginary).toBe(expectedImaginary);
});
});
it('Should correctly calculate the magnitude', () => {
const dummyReal = Math.random();
const dummyImaginary = Math.random();
const expected = Math.sqrt(
dummyReal * dummyReal + dummyImaginary * dummyImaginary
);
const actual = new Complex(dummyReal, dummyImaginary).magnitude();
console.dir({ dummyReal, dummyImaginary, actual, expected });
expect(actual).toBe(expected);
});
describe('Equality', () => {
it('should return true when equal', () => {
const complex1 = new Complex(Math.random(), Math.random());
const complex2 = new Complex(complex1.real, complex1.imaginary);
const actual = complex1.equals(complex2);
expect(actual).toBe(true);
});
it('should return false when real part differs', () => {
const complex1 = new Complex(Math.random(), Math.random());
const complex2 = new Complex(complex1.real + 1, complex1.imaginary);
const actual = complex1.equals(complex2);
expect(actual).toBe(false);
});
it('should return true when equal', () => {
const complex1 = new Complex(Math.random(), Math.random());
const complex2 = new Complex(complex1.real, complex1.imaginary + 1);
const actual = complex1.equals(complex2);
expect(actual).toBe(false);
});
});
describe('Arithmetic', () => {
const complex1 = new Complex(6, 3);
const complex2 = new Complex(7, -5);
describe('Add', () => {
const expectedReal = complex1.real + complex2.real;
const expectedImaginary = complex1.imaginary + complex2.imaginary;
const actual = complex1.add(complex2);
it(`Real part should be ${expectedReal}`, () => {
expect(actual.real).toBe(expectedReal);
});
it(`Imaginary part should be ${expectedImaginary}`, () => {
expect(actual.imaginary).toBe(expectedImaginary);
});
});
describe('Subtract', () => {
const expectedReal = complex1.real - complex2.real;
const expectedImaginary = complex1.imaginary - complex2.imaginary;
const actual = complex1.subtract(complex2);
it(`Real part should be ${expectedReal}`, () => {
expect(actual.real).toBe(expectedReal);
});
it(`Imaginary part should be ${expectedImaginary}`, () => {
expect(actual.imaginary).toBe(expectedImaginary);
});
});
describe('Multiply', () => {
const expectedReal = complex1.real + complex2.real;
const expectedImaginary = complex1.imaginary + complex2.imaginary;
const actual = complex1.add(complex2);
it(`Real part should be ${expectedReal}`, () => {
expect(actual.real).toBe(expectedReal);
});
it(`Imaginary part should be ${expectedImaginary}`, () => {
expect(actual.imaginary).toBe(expectedImaginary);
});
});
describe('Divide', () => {
const expectedReal = 27 / 74;
const expectedImaginary = 51 / 74;
const actual = complex1.divide(complex2);
it(`should have correct real`, () => {
expect(actual.real).toBe(expectedReal);
});
it(`should have correct imaginary`, () => {
expect(actual.imaginary).toBe(expectedImaginary);
});
});
});
describe('toString()', () => {
it('for positive imaginary part', () => {
const dummyReal = 1;
const dummyImaginary = 1;
const expected = `${dummyReal} + ${dummyImaginary}i`;
const actual = new Complex(dummyReal, dummyImaginary).toString();
expect(actual).toBe(expected);
});
it('for zero imaginary part', () => {
const dummyReal = 1;
const dummyImaginary = 0;
const expected = `${dummyReal} + ${dummyImaginary}i`;
const actual = new Complex(dummyReal, dummyImaginary).toString();
expect(actual).toBe(expected);
});
it('for negative imaginary part', () => {
const dummyReal = 1;
const dummyImaginary = -1;
const expected = `${dummyReal} - ${Math.abs(dummyImaginary)}i`;
const actual = new Complex(dummyReal, dummyImaginary).toString();
expect(actual).toBe(expected);
});
});
Ich muss zugeben, es hat Spaß gemacht, diese Ableitungen wiederzuentdecken, ich habe mich schon lange nicht mehr mit Vektorrechnung beschäftigt.
Das war nicht gerade ein aufregender Artikel, er ist sehr trocken, aber die Funktionen sind wichtig, wenn Sie mit Mandelbrot- oder Julia-Mengen oder allem spielen wollen, was komplexe Zahlen benötigt.
Danke fürs Lesen.

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































