-
-
Save WoolDoughnut310/8b5e017c15396a1169f14a0c872177c3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const getElements = (compounds) => { | |
let elements = []; | |
for (let compound of compounds) { | |
for (let element of Object.keys(compound)) { | |
if (!elements.includes(element)) elements.push(element); | |
} | |
} | |
elements.sort(); | |
return elements; | |
} | |
const constructElementMatrix = (elements, reactants, products) => { | |
let matrix = []; | |
for (let i = 0; i < elements.length; i++) { | |
const element = elements[i]; | |
for (let j = 0; j < reactants.length; j++) { | |
const reactant = reactants[j]; | |
if (matrix[i] === undefined) matrix[i] = []; | |
matrix[i][j] = new Fraction(reactant[element] ?? 0); | |
} | |
for (let j = 0; j < products.length; j++) { | |
const product = products[j]; | |
matrix[i][j + reactants.length] = new Fraction( | |
-(product[element] ?? 0) | |
); | |
} | |
} | |
return matrix; | |
} | |
const forwardElimination = (matrix) => { | |
// Number of rows | |
let n = matrix.length; | |
// Number of columns | |
let m = matrix[0].length; | |
for (let i = 0; i < n; i++) { | |
// Identifying maximum value and position for a pivot | |
let iMax = i; | |
let vMax = matrix[iMax][i]; | |
for (let j = i + 1; j < n; j++) { | |
if (matrix[j][i].abs() > vMax) { | |
vMax = matrix[j][i]; | |
iMax = j; | |
} | |
} | |
if (iMax !== i) { | |
// Swap the current row with the maximum value's row | |
for (let k = 0; k < m; k++) { | |
let temp = matrix[i][k]; | |
matrix[i][k] = matrix[iMax][k]; | |
matrix[iMax][k] = temp; | |
} | |
} | |
for (let j = i + 1; j < n; j++) { | |
const ratio = matrix[j][i].div(matrix[i][i]); | |
let empty = true; | |
for (let k = 0; k < m; k++) { | |
matrix[j][k] = matrix[j][k].sub(ratio.mul(matrix[i][k])); | |
if (!matrix[j][k].equals(0)) empty = false; | |
} | |
// Delete empty rows to avoid later zero division | |
if (empty) { | |
n--; | |
delete matrix[j]; | |
} | |
} | |
} | |
return matrix; | |
} | |
const backSubstitution = (matrix) => { | |
let solution = new Array(n).fill(new Fraction(0)); | |
// Applying back substitution | |
solution[n - 1] = matrix[n - 1][m - 1].div(matrix[n - 1][m - 2]); | |
for (let i = n - 2; i >= 0; i--) { | |
solution[i] = matrix[i][n]; | |
for (let j = i + 1; j < m; j++) { | |
solution[i] = solution[i].sub(matrix[i][j].mul(solution[j])); | |
} | |
solution[i] = solution[i].div(matrix[i][i]); | |
} | |
solution.push(new Fraction(1)); | |
for (let i = 0; i < solution.length; i++) { | |
solution[i] = solution[i].abs(); | |
if (!solution[i].equals(parseFloat(solution[i].toString()) | 0)) { | |
let scale = solution[i].inverse().lcm(1); | |
for (let j = 0; j < solution.length; j++) { | |
solution[j] = solution[j].mul(scale).abs(); | |
} | |
} | |
} | |
} | |
const balanceEquation = ( | |
reactants, | |
products | |
) => { | |
let elements = getElements(reactants); | |
let matrix = constructElementMatrix(elements, reactants, products); | |
forwardElimination(matrix); | |
let solution = backSubstitution(matrix); | |
// Converting Fraction objects back to numbers | |
solution = solution.map(Number); | |
return [ | |
solution.slice(0, reactants.length), | |
solution.slice(reactants.length), | |
]; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment