Bilangan Kompleks dalam JavaScript

Monster imajiner yang memikirkan bilangan real. (Gambar: DALL-E)

Perkenalan

Saya telah bermain dengan Mandelbrot Set akhir-akhir ini, yang berarti saya harus menghilangkan ingatan saya tentang bagaimana melakukan aritmatika dengan bilangan kompleks. Saya membuat bantuan kelas JavaScript yang bagus untuk menggunakannya. Ini dia sekarang berfungsi (ini penjelasan yang cukup kering, tapi saya harap ini berguna).

Penyegaran Cepat

Saat kita mengalikan dua angka, hasilnya akan ganjil atau genap tergantung pada apakah angka yang kita kalikan adalah:

-1 *  1 = -1
 1 * -1 = -1
 1 *  1 =  1
-1 * -1 =  1

i  ≔ √(-1)
∴ i² =  -1

Bilangan kompleks adalah bilangan yang memiliki bagian nyata dan bagian imajiner . Mereka ditulis dalam bentuk a + bi.

Karena 0i = 0garis bilangan bertemu di 0, sehingga kita dapat memplot bilangan ini pada grafik, biasanya sumbu x adalah garis bilangan real, dan sumbu y adalah sumbu imajiner.

Beberapa contoh

3 + 2i diplot pada bidang kompleks
3–8i diplot pada bidang kompleks
-15 + 9i diplot pada bidang kompleks

Hitung

Anda mungkin telah memperhatikan bahwa bilangan kompleks sangat mirip dengan vektor, dan dengan peringatan bahwa i * i = -1memang demikian. Semua operasi hanyalah operasi vektor biasa yang disesuaikan berdasarkan peringatan itu.

Kode

Konstruktor Complexkelas memiliki dua parameter.

  • real
  • imaginary
  • 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
 );
}

Penurunan rumus perkalian

/***
 * 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);
}

Kelas Aplikasi

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 digunakan di sini.

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);
    });
});

Harus saya akui, menemukan kembali derivasi itu cukup menyenangkan, saya sudah lama tidak mengerjakan matematika vektor.

Ini bukan artikel yang menarik, sangat kering tetapi fungsinya penting jika Anda ingin bermain dengan set Mandelbrot atau Julia atau apa pun yang membutuhkan bilangan kompleks.

Terima kasih sudah membaca.

Suggested posts

Bagaimana saya mentransfer dari Math ke CS

Bagaimana saya mentransfer dari Math ke CS

Proses penerimaan Musim Gugur 2023 akan segera berakhir, dan penangguhan/penolakan sedang terjadi. Selamat jika Anda telah diterima di program impian Anda di UW.

Sumber daya pengkodean saya yang paling sering digunakan

Sumber daya pengkodean saya yang paling sering digunakan

Minggu ini, saya menerima email dari anggota daftar email harian. Mereka ingin tahu: Sumber daya apa yang Anda kunjungi setiap hari, setiap minggu, dan setiap bulan? Pertanyaan bagus! Saya senang untuk mencoba membagikan beberapa wawasan tentang alur kerja saya & sumber daya teratas yang saya gunakan.

Related posts

Memikirkan Kembali Media: Lima Kebenaran McChesney dan Masa Depan Komunikasi

Di dunia yang sangat terhubung saat ini, sistem media memainkan peran penting dalam membentuk opini publik, dan akibatnya, dalam pengambilan keputusan yang demokratis. Dalam analisisnya yang mendalam tentang ekonomi politik komunikasi, cendekiawan Robert McChesney menantang kebijaksanaan konvensional dan menyajikan apa yang disebutnya sebagai “lima kebenaran” tentang media.

Dua tahun dalam desain- Yang baik, buruk, dan jelek…

Dua tahun dalam desain- Yang baik, buruk, dan jelek…

“Hai Siri, mainkan Me Dive oleh Olivia Dean.” Saat itu Mei 2021 ketika saya memulai magang di Future Academy Africa di Akure, Nigeria.

Jurnal Ouvert

Jurnal Ouvert

Buku Harian Terbuka: Gambar Sehari. Saya selalu terpesona oleh cerita.

Tantangan Membaca Goodreads Beracun

Tantangan Membaca Goodreads Beracun

Gatal di tangan saya menyebar saat saya menawar dengan penjual buku lokal untuk buku lain. Buku ini seperti buku lain yang saya beli baru-baru ini akan menemukan singgasananya di tumpukan 'To Be Read' (TBR) saya; namun, saya membelinya.