Numeri complessi in JavaScript
introduzione
Ultimamente ho giocato con l'insieme di Mandelbrot, il che significa che ho dovuto rispolverare la mia memoria su come fare aritmetica con numeri complessi. Ho creato una bella classe JavaScript che aiuta a ingrassare usandoli. Ecco ora funziona (è una spiegazione abbastanza asciutta, ma spero sia utile).
Un rapido aggiornamento
Quando moltiplichiamo due numeri insieme il risultato sarà pari o dispari a seconda che i numeri che stiamo moltiplicando siano:
-1 * 1 = -1
1 * -1 = -1
1 * 1 = 1
-1 * -1 = 1
i ≔ √(-1)
∴ i² = -1
Un numero complesso è un numero che ha una parte reale e una parte immaginaria . Sono scritti nel form a + bi.
Poiché 0i = 0le linee dei numeri si incontrano in 0, quindi possiamo tracciare questi numeri su un grafico, in genere l'asse x è la linea dei numeri reali e l'asse y è quella immaginaria.
Qualche esempio
Aritmetica
Potresti aver notato che i numeri complessi assomigliano molto ai vettori e con l'avvertenza che i * i = -1lo sono. Tutte le operazioni sono solo normali operazioni vettoriali che vengono regolate in base a tale avvertenza.
Codice
Il Complexcostruttore di classi ha due parametri.
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);
}
Classe di applicazione
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;
}
}
Vitest è usato qui.
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);
});
});
Devo ammettere che è stato abbastanza divertente riscoprire quelle derivazioni, non ho fatto matematica vettoriale per molto tempo.
Questo non era esattamente un articolo entusiasmante, è molto secco ma le funzioni sono importanti se vuoi giocare con gli insiemi di Mandelbrot o Julia o qualsiasi cosa richieda numeri complessi.
Grazie per aver letto.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































