laplace-determinant
Version:
recursive determinant computation using Laplace expansion
116 lines (95 loc) • 2.84 kB
JavaScript
var no = require('not-defined')
/**
* Convert a pair of indices to a 1-dimensional index
*
* @function
* @param {Number} i index row
* @param {Number} j index column
* @param {Number} numCols
*
* @returns {Number} index
*/
function matrixToArrayIndex (i, j, numCols) {
return j + i * numCols
}
/**
* Compute the sub-matrix formed by deleting the i-th row and j-th column
*
* @function
*
* @param {Array} data set
* @param {Number} numRows
* @param {Number} numCols
* @param {Number} row index deleted
* @param {Number} col index deleted
*
* @returns {Array} sub data-set
*/
function subMatrix (data, numRows, numCols, row, col) {
var sub = []
for (var i = 0; i < numRows; i++) {
for (var j = 0; j < numCols; j++) {
if ((i !== row) && (j !== col)) {
sub.push(data[matrixToArrayIndex(i, j, numCols)])
}
}
}
return sub
}
/**
* Computes the determinant of a matrix using Laplace's formula
*
* See https://en.wikipedia.org/wiki/Laplace_expansion
*
* @function
*
* @param {Array} data, lenght must be a square.
* @param {Object} [scalar]
* @param {Function} [scalar.addition = (a, b) -> a + b ]
* @param {Function} [scalar.multiplication = (a, b) -> a * b ]
* @param {Function} [scalar.negation = (a) -> -a ]
* @param {Number} [order], defaults to Math.sqrt(data.length)
*
* @returns {*} det
*/
function determinant (data, scalar, order) {
// Recursion will stop here:
// the determinant of a 1x1 matrix is its only element.
if (data.length === 1) return data[0]
if (no(order)) order = Math.sqrt(data.length)
if (order % 1 !== 0) {
throw new TypeError('data.lenght must be a square')
}
// Default to common real number field.
if (no(scalar)) {
scalar = {
addition: function (a, b) { return a + b },
multiplication: function (a, b) { return a * b },
negation: function (a) { return -a }
}
}
var det
// TODO choose best row or column to start from, i.e. the one with more zeros
// by now we start from first row, and walk by column
// needs scalar.isZero
//
// is scalar.isZero is a function will be used, but should remain optional
var startingRow = 0
for (var col = 0; col < order; col++) {
var subData = subMatrix(data, order, order, startingRow, col)
// +-- Recursion here.
// ↓
var cofactor = determinant(subData, scalar, order - 1)
if ((startingRow + col) % 2 === 1) {
cofactor = scalar.negation(cofactor)
}
var index = matrixToArrayIndex(startingRow, col, order)
if (no(det)) {
det = scalar.multiplication(data[index], cofactor) // first iteration
} else {
det = scalar.addition(det, scalar.multiplication(data[index], cofactor))
}
}
return det
}
module.exports = determinant