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) + }) +})
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: