La eliminación de Gauss transforma un sistema lineal en una forma escalonada equivalente. Después, una sustitución hacia atrás permite calcular las incógnitas desde la última ecuación hasta la primera.
Resolver un sistema por sustitución directa se vuelve difícil cuando aumenta la cantidad de ecuaciones. La eliminación de Gauss organiza el trabajo mediante operaciones sistemáticas sobre las filas.
El método tiene dos fases:
El sistema se representa mediante [A | b]. Cada fila contiene los coeficientes de una ecuación y su término independiente.
function aumentar(A, b) {
if (A.length !== b.length) throw new Error("Dimensiones incompatibles");
return A.map((fila, i) => [...fila, b[i]]);
}
console.log(aumentar([[1, 1], [2, -1]], [5, 1]));
Las siguientes operaciones producen sistemas equivalentes, es decir, conservan el conjunto de soluciones:
const matriz = [[1, 1, 5], [2, -1, 1]];
// F2 recibe F2 - 2F1.
matriz[1] = matriz[1].map(
(valor, j) => valor - 2 * matriz[0][j]
);
console.log(matriz); // [[1, 1, 5], [0, -3, -9]]
Una matriz se encuentra en forma escalonada cuando:
Esta forma representa un sistema triangular, fácil de resolver comenzando por la última ecuación.
El pivote es el coeficiente utilizado para eliminar los valores que están debajo de él. Si el candidato es cero, debe intercambiarse la fila con otra que tenga un valor no nulo en esa columna.
En cálculo numérico se aplica pivoteo parcial: se selecciona el elemento de mayor valor absoluto entre las filas disponibles.
const matriz = [[0.001, 2, 3], [4, 5, 6], [-2, 1, 0]];
const columna = 0;
let filaPivote = 0;
for (let fila = 1; fila < matriz.length; fila++) {
if (Math.abs(matriz[fila][columna]) > Math.abs(matriz[filaPivote][columna])) {
filaPivote = fila;
}
}
console.log(filaPivote); // 1, porque |4| es el mayor
Para anular el elemento aik situado debajo del pivote akk, se calcula el factor:
const pivote = [2, 1, 5];
const fila = [6, 4, 17];
const factor = fila[0] / pivote[0];
const nuevaFila = fila.map((valor, j) => valor - factor * pivote[j]);
console.log(factor); // 3
console.log(nuevaFila); // [0, 1, 2]
Partimos del sistema x + y = 5, 2x − y = 1:
La segunda fila indica −3y = −9, entonces y = 3. La primera ecuación produce x = 5 − 3 = 2.
const fila1 = [1, 1, 5];
const fila2 = [0, -3, -9];
const y = fila2[2] / fila2[1];
const x = (fila1[2] - fila1[1] * y) / fila1[0];
console.log({ x, y }); // { x: 2, y: 3 }
Consideremos:
La matriz aumentada inicial es [[1, 1, 1 | 6], [2, −1, 1 | 3], [1, 2, −1 | 2]]. Primero aplicamos F2 − 2F1 y F3 − F1.
const F1 = [1, 1, 1, 6];
const F2 = [2, -1, 1, 3].map((v, j) => v - 2 * F1[j]);
const F3 = [1, 2, -1, 2].map((v, j) => v - F1[j]);
console.log(F2); // [0, -3, -1, -9]
console.log(F3); // [0, 1, -2, -4]
Conviene intercambiar las filas segunda y tercera para utilizar el pivote 1. Después se aplica F3 ← F3 + 3F2:
Ahora el sistema está escalonado y listo para la sustitución hacia atrás.
const fila2 = [0, 1, -2, -4];
const fila3Original = [0, -3, -1, -9];
const fila3 = fila3Original.map((v, j) => v + 3 * fila2[j]);
console.log(fila3); // [0, 0, -7, -21]
Se resuelve desde la última fila hacia la primera:
const z = -21 / -7;
const y = -4 + 2 * z;
const x = 6 - y - z;
console.log({ x, y, z }); // { x: 1, y: 2, z: 3 }
Para una matriz aumentada triangular de n filas, cada incógnita se calcula restando los términos ya conocidos y dividiendo por el coeficiente diagonal.
function sustitucionAtras(matriz, tolerancia = 1e-12) {
const n = matriz.length;
const x = Array(n).fill(0);
for (let i = n - 1; i >= 0; i--) {
if (Math.abs(matriz[i][i]) <= tolerancia) return null;
let restante = matriz[i][n];
for (let j = i + 1; j < n; j++) {
restante -= matriz[i][j] * x[j];
}
x[i] = restante / matriz[i][i];
}
return x;
}
const triangular = [[1, 1, 1, 6], [0, 1, -2, -4], [0, 0, -7, -21]];
console.log(sustitucionAtras(triangular)); // [1, 2, 3]
Esta función escalona una matriz aumentada cuadrada. El pivoteo parcial se realiza antes de eliminar cada columna.
function escalonar(aumentada, tolerancia = 1e-12) {
const m = aumentada.map(fila => fila.map(Number));
const n = m.length;
for (let k = 0; k < n; k++) {
let mejor = k;
for (let i = k + 1; i < n; i++) {
if (Math.abs(m[i][k]) > Math.abs(m[mejor][k])) mejor = i;
}
if (Math.abs(m[mejor][k]) <= tolerancia) return null;
[m[k], m[mejor]] = [m[mejor], m[k]];
for (let i = k + 1; i < n; i++) {
const factor = m[i][k] / m[k][k];
for (let j = k; j <= n; j++) {
m[i][j] -= factor * m[k][j];
}
}
}
return m;
}
const sistema = [[1, 1, 1, 6], [2, -1, 1, 3], [1, 2, -1, 2]];
console.log(escalonar(sistema));
Podemos reunir la eliminación y la sustitución hacia atrás en una sola función. Esta versión resuelve sistemas cuadrados con solución única.
function resolverGauss(A, b, tolerancia = 1e-12) {
const n = A.length;
if (n === 0 || b.length !== n || A.some(fila => fila.length !== n)) {
throw new Error("Se requiere un sistema cuadrado");
}
const m = A.map((fila, i) => [...fila.map(Number), Number(b[i])]);
for (let k = 0; k < n; k++) {
let mejor = k;
for (let i = k + 1; i < n; i++) {
if (Math.abs(m[i][k]) > Math.abs(m[mejor][k])) mejor = i;
}
if (Math.abs(m[mejor][k]) <= tolerancia) return null;
[m[k], m[mejor]] = [m[mejor], m[k]];
for (let i = k + 1; i < n; i++) {
const factor = m[i][k] / m[k][k];
for (let j = k; j <= n; j++) m[i][j] -= factor * m[k][j];
}
}
const x = Array(n).fill(0);
for (let i = n - 1; i >= 0; i--) {
let valor = m[i][n];
for (let j = i + 1; j < n; j++) valor -= m[i][j] * x[j];
x[i] = valor / m[i][i];
}
return x;
}
const A = [[1, 1, 1], [2, -1, 1], [1, 2, -1]];
console.log(resolverGauss(A, [6, 3, 2])); // [1, 2, 3]
Un algoritmo debe comprobarse sustituyendo la solución obtenida en Ax = b. Por los errores decimales, se comparan los valores con una tolerancia.
function verificar(A, x, b, tolerancia = 1e-10) {
return A.every((fila, i) => {
const valor = fila.reduce((suma, a, j) => suma + a * x[j], 0);
return Math.abs(valor - b[i]) < tolerancia;
});
}
const A = [[1, 1, 1], [2, -1, 1], [1, 2, -1]];
console.log(verificar(A, [1, 2, 3], [6, 3, 2])); // true
Durante la eliminación pueden aparecer filas que revelan el tipo de sistema:
| Fila escalonada | Interpretación |
|---|---|
| [0, 0, ..., 0 | 0] | Ecuación redundante; puede haber variables libres |
| [0, 0, ..., 0 | c], c ≠ 0 | Contradicción; el sistema no tiene solución |
| Un pivote por cada incógnita | Solución única |
function tipoFila(fila, tolerancia = 1e-12) {
const coeficientes = fila.slice(0, -1);
const termino = fila.at(-1);
const todosCero = coeficientes.every(v => Math.abs(v) <= tolerancia);
if (!todosCero) return "fila con pivote";
return Math.abs(termino) <= tolerancia ? "redundante" : "contradicción";
}
console.log(tipoFila([0, 0, 5])); // contradicción
Cada pivote obtenido durante la eliminación corresponde a una fila independiente. Por eso el número de pivotes de A es rango(A).
Comparar los pivotes de A con los de [A | b] permite distinguir:
Para una matriz cuadrada de orden n, la eliminación requiere una cantidad de operaciones proporcional a n3. La sustitución hacia atrás cuesta aproximadamente n2.
El costo cúbico explica por qué las matrices muy grandes requieren implementaciones optimizadas, estructuras dispersas o métodos especializados.
function crecimientoAproximado(n) {
return n ** 3;
}
console.log(crecimientoAproximado(10)); // 1000
console.log(crecimientoAproximado(100)); // 1000000
El pivoteo parcial ayuda a controlar el error numérico, pero una matriz mal condicionada aún puede producir soluciones sensibles a pequeñas variaciones.
Errores habituales:
La eliminación de Gauss convierte un sistema en una estructura triangular mediante operaciones sencillas y repetibles. Su combinación con la sustitución hacia atrás forma un algoritmo general para sistemas con solución única.
En el próximo tema estudiaremos Gauss-Jordan, que continúa la reducción hasta eliminar también los valores situados sobre cada pivote.