diff --git a/maths/determinant.ts b/maths/determinant.ts new file mode 100644 index 00000000..456154aa --- /dev/null +++ b/maths/determinant.ts @@ -0,0 +1,73 @@ +/** + * @description + * Computes the determinant of the given matrix using elimination. + * - Rounding errors may occur for some matrices. + * - Only handles 6 decimal places. Rounds thereafter. + * @Complexity_Analysis + * Time complexity: O(n^3) + * Space Complexity: O(n^2) + * @param {number[][]} m - A square matrix (2D array) + * @return {number} - The determinant + * @example det([[1,1],[1,1]]) = 0 + */ + +function interchange(m: number[][], from: number, to: number): number[][] { + ;[m[to], m[from]] = [m[from], m[to]] + return m +} + +function addition( + m: number[][], + from: number, + to: number, + c: number +): number[][] { + m[to] = m[to].map((e, i) => e + c * m[from][i]) + return m +} + +function diagProduct(m: number[][]): number { + let product = 1 + for (let i = 0; i < m.length; i++) { + product *= m[i][i] + } + return product +} + +export function det(m: number[][]): number { + if (m.some((r) => r.length != m.length)) { + throw new Error('only square matrices can have determinants') + } + + const decPlaces = 6 + const epsilon = 1e-6 + + // track the number of applied interchange operations + let appliedICs = 0 + for (let i = 0; i < m[0].length; i++) { + // partial pivotting + let idealPivot = null + let maxValue = 0 + for (let j = i; j < m.length; j++) { + if (Math.abs(m[j][i]) > maxValue) { + maxValue = Math.abs(m[j][i]) + idealPivot = j + } + } + if (idealPivot === null) { + return 0 + } + if (idealPivot != i) { + m = interchange(m, i, idealPivot) + appliedICs++ + } + // eliminate entries under the pivot + for (let j = i + 1; j < m.length; j++) { + if (Math.abs(m[j][i]) > epsilon) { + m = addition(m, i, j, -m[j][i] / m[i][i]) + } + } + } + const result = diagProduct(m) * (-1) ** appliedICs + return parseFloat(result.toFixed(decPlaces)) +} diff --git a/maths/test/determinant.test.ts b/maths/test/determinant.test.ts new file mode 100644 index 00000000..c55e8718 --- /dev/null +++ b/maths/test/determinant.test.ts @@ -0,0 +1,89 @@ +import { det } from '../determinant' + +describe('determinant', () => { + test.each([ + [ + [ + [1, 2], + [3, 4, 5] + ] + ], + [ + [ + [1, 2, 3], + [4, 5, 6] + ] + ], + [ + [ + [1, 2], + [3, 4], + [5, 6] + ] + ] + ])('should throw an error for non square matrix %p', (matrix) => { + expect(() => det(matrix)).toThrow( + 'only square matrices can have determinants' + ) + }) + + test.each([ + [ + [ + [1, 2], + [3, 4] + ], + -2 + ], + [ + [ + [1, 1], + [1, 1] + ], + 0 + ], + [ + [ + [1, 2], + [0, 0] + ], + 0 + ], + [ + [ + [8, 1, 5], + [9, 3, 7], + [1, 4, 4] + ], + 8 + ], + [ + [ + [15, 85, 32], + [76, 83, 23], + [28, 56, 92] + ], + -382536 + ], + [ + [ + [2, -1, 0, 3], + [4, 0, 1, 2], + [3, 2, -1, 1], + [1, 3, 2, -2] + ], + -42 + ], + [ + [ + [0.75483643, 0.68517541, 0.53548329, 0.5931435], + [0.37031247, 0.80103707, 0.82563949, 0.91266224], + [0.39293451, 0.27228353, 0.54093836, 0.51963319], + [0.60997323, 0.40161682, 0.58330774, 0.17392144] + ], + -0.051073 + ] + ])('determinant of %p should be %d', (matrix, expected) => { + expect(det(matrix)).toBe(expected) + }) +}) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy