Álgebra lineal numérica · Tema 16

Pivoteo y factorización LU

Cómo elegir pivotes más seguros, registrar los intercambios de filas y reutilizar la eliminación de Gauss para resolver varios sistemas con la misma matriz.

01 · Mejorar la eliminación

El orden de las filas importa numéricamente

La eliminación de Gauss divide por los pivotes de la diagonal. Un pivote cero detiene el algoritmo básico y uno muy pequeño puede producir multiplicadores enormes, aunque el sistema tenga una solución única.

mik = aik / akk

Si |a[k][k]| es diminuto, pequeñas perturbaciones de los datos o del redondeo pueden amplificarse. Intercambiar filas antes de dividir permite elegir un coeficiente más apropiado.

02 · Motivación

Cuando el primer pivote es cero

Consideremos la matriz aumentada:

[ 0   2 | 4 ;   1   1 | 3 ]

No podemos calcular m₂₁=1/0. Sin embargo, intercambiando las filas:

F₁ ↔ F₂    ⇒    [ 1   1 | 3 ;   0   2 | 4 ]

La solución se obtiene inmediatamente: y=2 y x=1. El cero inicial era un problema del orden, no una prueba de singularidad.

El mismo principio se aplica a pivotes no nulos pero demasiado pequeños.

03 · Estrategia principal

Pivoteo parcial

En la etapa k, el pivoteo parcial busca el elemento de mayor valor absoluto en la columna k, desde la fila k hacia abajo:

p = arg maxi=k,…,n−1 |aik|

Si p≠k, intercambia las filas p y k. Después continúa la eliminación normalmente.

  1. 1
    Buscar.

    Recorrer la parte activa de la columna y localizar la mayor magnitud.

  2. 2
    Comprobar.

    Si incluso el mejor candidato es numéricamente cero, la matriz es singular para la tolerancia usada.

  3. 3
    Intercambiar.

    Mover la fila seleccionada a la posición del pivote.

  4. 4
    Eliminar.

    Calcular multiplicadores y anular los elementos inferiores.

Esta política añade búsquedas de costo O(n²), inferior al costo cúbico de la eliminación, y suele ofrecer una estabilidad muy buena en la práctica.

04 · Variantes

Pivoteo escalado y pivoteo completo

Estrategias para seleccionar el pivote.
EstrategiaSelecciónVentajaCosto o dificultad
Sin pivoteoElemento diagonalImplementación mínimaPuede fallar o ser inestable
ParcialMayor |aᵢₖ| de la columnaRobusto y económicoRegistra intercambios de filas
Parcial escaladoMayor |aᵢₖ|/sᵢConsidera la escala de cada filaCalcula y actualiza escalas
CompletoMayor elemento de la submatrizControl adicionalIntercambia filas y columnas

En el pivoteo escalado se suele definir sᵢ=maxⱼ|aᵢⱼ|. El pivoteo completo requiere registrar también una permutación de variables y conduce a una relación del tipo PAQ=LU.

05 · Registrar intercambios

La matriz de permutación P

Una matriz de permutación se obtiene al reordenar las filas de la identidad. Multiplicar P por A aplica a A esos mismos intercambios.

P = [ 0   1   0 ;   1   0   0 ;   0   0   1 ]

La matriz anterior intercambia las dos primeras filas. En código no hace falta almacenar todos sus ceros y unos: un vector [1,0,2] indica que la nueva fila 0 proviene de la fila 1, la nueva fila 1 de la fila 0 y la tercera permanece igual.

function permutarVector(permutacion, b) {
  if (permutacion.length !== b.length) {
    throw new Error("Dimensiones incompatibles");
  }
  return permutacion.map(indice => b[indice]);
}

console.log(permutarVector([1, 0, 2], [10, 20, 30]));
// [20, 10, 30]

06 · Separar el trabajo

Qué significa factorizar A=LU

La eliminación puede expresarse como el producto de dos matrices triangulares:

A = LU
L: triangular inferior
Contiene unos en la diagonal y los multiplicadores utilizados durante la eliminación.
U: triangular superior
Es la matriz que queda después de la eliminación hacia adelante.

Si hubo intercambios de filas, la relación correcta es:

PA = LU

Esta forma separa el trabajo costoso de triangularización de las sustituciones económicas que dependen del término independiente.

07 · Construcción manual

Guardar los multiplicadores en L

Consideremos una matriz que no necesita intercambios para este ejemplo:

A = [ 2   1   1 ;   4   −6   0 ;   −2   7   2 ]

Los primeros multiplicadores son m₂₁=2 y m₃₁=−1. Después de la primera columna, el multiplicador de la segunda etapa es m₃₂=−1. El resultado es:

L = [ 1   0   0 ;   2   1   0 ;   −1   −1   1 ]
U = [ 2   1   1 ;   0   −8   −2 ;   0   0   1 ]

Al multiplicar LU recuperamos exactamente A. Los números que Gauss usa temporalmente como multiplicadores se convierten en información reutilizable.

08 · Dos sustituciones

Resolver Ax=b a partir de LU

Si PA=LU, entonces:

Ax=b   ⇒   PAx=Pb   ⇒   LUx=Pb

Introducimos un vector intermedio y y resolvemos dos sistemas triangulares:

Pb→Ly=Pb→Ux=y→x
  1. Sustitución progresiva: resolver Ly=Pb desde la primera fila hacia abajo.
  2. Sustitución regresiva: resolver Ux=y desde la última fila hacia arriba.

Para la matriz del ejemplo y b=[7,−8,18]ᵀ, obtenemos y=[7,−22,3]ᵀ y finalmente x=[1,2,3]ᵀ.

09 · Algoritmo principal

Factorización LU con pivoteo parcial

Para ahorrar memoria, podemos guardar U en la diagonal y parte superior de una sola matriz, y los multiplicadores de L debajo de la diagonal. Los unos de L quedan implícitos.

function factorizarLU(A, tolerancia = 1e-12) {
  const n = A.length;
  if (n === 0 || A.some(fila => fila.length !== n)) {
    throw new Error("A debe ser una matriz cuadrada no vacía");
  }
  if (A.some(fila => fila.some(valor => !Number.isFinite(valor)))) {
    throw new Error("Los coeficientes deben ser finitos");
  }

  const LU = A.map(fila => [...fila]);
  const P = Array.from({ length: n }, (_, i) => i);
  const escala = Math.max(...A.flat().map(Math.abs));
  if (escala === 0) throw new Error("La matriz es singular");
  let intercambios = 0;

  for (let k = 0; k < n; k++) {
    let filaPivote = k;
    for (let i = k + 1; i < n; i++) {
      if (Math.abs(LU[i][k]) > Math.abs(LU[filaPivote][k])) {
        filaPivote = i;
      }
    }
    if (Math.abs(LU[filaPivote][k]) <= tolerancia * escala) {
      throw new Error("La matriz es singular o casi singular");
    }
    if (filaPivote !== k) {
      [LU[k], LU[filaPivote]] = [LU[filaPivote], LU[k]];
      [P[k], P[filaPivote]] = [P[filaPivote], P[k]];
      intercambios++;
    }

    for (let i = k + 1; i < n; i++) {
      LU[i][k] /= LU[k][k];
      for (let j = k + 1; j < n; j++) {
        LU[i][j] -= LU[i][k] * LU[k][j];
      }
    }
  }
  return { LU, P, intercambios };
}

const ejemploA = [[2, 1, 1], [4, -6, 0], [-2, 7, 2]];
console.log(factorizarLU(ejemploA));

Cuando se intercambian filas completas de la matriz combinada, también se reordenan los multiplicadores guardados en etapas anteriores.

10 · Reutilización

Resolver con una factorización existente

// Versión compacta para que el ejemplo sea ejecutable por separado.
function factorizarLU(A) {
  const LU = A.map(fila => [...fila]);
  const P = Array.from({ length: A.length }, (_, i) => i);

  for (let k = 0; k < A.length; k++) {
    let pivote = k;
    for (let i = k + 1; i < A.length; i++) {
      if (Math.abs(LU[i][k]) > Math.abs(LU[pivote][k])) pivote = i;
    }
    if (Math.abs(LU[pivote][k]) <= 1e-12) {
      throw new Error("Matriz singular o casi singular");
    }
    [LU[k], LU[pivote]] = [LU[pivote], LU[k]];
    [P[k], P[pivote]] = [P[pivote], P[k]];
    for (let i = k + 1; i < A.length; i++) {
      LU[i][k] /= LU[k][k];
      for (let j = k + 1; j < A.length; j++) {
        LU[i][j] -= LU[i][k] * LU[k][j];
      }
    }
  }
  return { LU, P };
}

function resolverLU(factorizacion, b) {
  const { LU, P } = factorizacion;
  const n = LU.length;
  if (b.length !== n || b.some(valor => !Number.isFinite(valor))) {
    throw new Error("Vector b inválido");
  }

  const pb = P.map(indice => b[indice]);
  const y = Array(n).fill(0);
  for (let i = 0; i < n; i++) {
    let suma = 0;
    for (let j = 0; j < i; j++) suma += LU[i][j] * y[j];
    y[i] = pb[i] - suma; // La diagonal de L contiene unos.
  }

  const x = Array(n).fill(0);
  for (let i = n - 1; i >= 0; i--) {
    let suma = 0;
    for (let j = i + 1; j < n; j++) suma += LU[i][j] * x[j];
    x[i] = (y[i] - suma) / LU[i][i];
  }
  return x;
}

const A = [[2, 1, 1], [4, -6, 0], [-2, 7, 2]];
const b = [7, -8, 18];
const factorizacion = factorizarLU(A);
console.log(resolverLU(factorizacion, b)); // [1, 2, 3]

La factorización cuesta O(n³), pero cada nuevo vector b necesita solamente dos sustituciones de costo O(n²).

11 · Aprovechar la factorización

Múltiples términos independientes y determinante

Varios vectores b

En simulaciones temporales, calibraciones o cálculos por escenarios, la matriz A puede permanecer fija mientras cambia b. Factorizar una vez evita repetir toda la eliminación.

Determinante

Como la diagonal de L contiene unos, det(L)=1. Cada intercambio de filas cambia el signo del determinante:

det(A) = (−1)s ∏i=0n−1 uii

Aquí s es la cantidad de intercambios. Este cálculo aprovecha la factorización sin expandir el determinante por cofactores.

Matriz inversa

Es posible resolver AX=I columna por columna usando la misma LU. Sin embargo, para resolver Ax=b no conviene construir explícitamente A⁻¹: las sustituciones son más directas.

12 · Control de calidad

Verificar PA≈LU y Ax≈b

Una implementación debe comprobar dos niveles diferentes:

  • Error de factorización: medir una norma de PA−LU.
  • Residuo de solución: medir una norma de b−Ax.
error relativo LU = ||PA−LU||∞ / ||A||∞
residuo relativo = ||b−Ax||∞ / (||A||∞||x||∞+||b||∞)

Las formas relativas permiten comparar problemas con escalas distintas. Un error pequeño confirma coherencia computacional, aunque no elimina el efecto de un mal condicionamiento.

13 · Rendimiento

Costo, almacenamiento y decisiones prácticas

Costos aproximados para matrices densas.
OperaciónTiempoComentario
Factorización LUO(n³)Se realiza una vez por matriz.
Sustitución progresivaO(n²)Una vez por cada b.
Sustitución regresivaO(n²)Una vez por cada b.
Pivoteo parcialO(n²) búsquedasNo cambia el orden cúbico total.
Almacenamiento combinadoO(n²)L y U comparten una matriz.

En software científico conviene utilizar bibliotecas optimizadas: aprovechan memoria caché, operaciones por bloques, paralelismo y rutinas numéricas ampliamente probadas.

14 · Exploración interactiva

Laboratorio: construir la matriz LU combinada

Elegí una matriz, una estrategia y una etapa. Sobre la diagonal se forma U; debajo se almacenan los multiplicadores de L. El laboratorio usa un vector b=A[1,2,−1]ᵀ para comprobar la solución.

EXPERIMENTO 16

Permutaciones, multiplicadores y pivotes

Matriz general 3×3

Los resultados numéricos aparecen debajo.
OperaciónMatriz inicial
Permutación P[0, 1, 2]
Solución calculadaPendiente
Residuo relativo—

Avanzá por las etapas para observar la factorización.

Preguntas para explorar

  1. En la matriz general, compará la permutación con y sin pivoteo.
  2. En «Pivote inicial cero», observá por qué el algoritmo básico se detiene.
  3. En «Filas con escalas distintas», compará pivoteo parcial y escalado.
  4. En la matriz singular, identificá la etapa donde desaparece un pivote válido.

15 · Comprensión

Confusiones frecuentes

«Toda matriz posee una factorización A=LU sin permutaciones»

No. Algunos órdenes producen pivotes nulos. Con pivoteo parcial, la forma general es PA=LU.

«L guarda las operaciones finales de U»

L guarda los multiplicadores utilizados para crear los ceros; U contiene el resultado triangular de la eliminación.

«Al intercambiar filas solo hay que cambiar U»

También deben actualizarse la permutación y los multiplicadores de columnas anteriores que ya forman parte de L.

«El pivoteo corrige una matriz mal condicionada»

Controla el crecimiento numérico del algoritmo, pero la sensibilidad matemática del problema permanece.

«LU es útil únicamente para un vector b»

Su mayor ventaja aparece cuando se resuelven muchos sistemas con la misma A y distintos términos independientes.

16 · Práctica guiada

Ejercicios

Ejercicio 1: elegir el pivote

En la columna activa [−0.2, 5, −3]ᵀ, ¿qué fila elige el pivoteo parcial?

Ver solución

Elige la fila del valor 5, porque tiene la mayor magnitud absoluta de la columna.

Ejercicio 2: interpretar P

¿Qué efecto tiene el vector de permutación [2,0,1] sobre b=[10,20,30]?

Ver solución

Produce Pb=[30,10,20]: la primera fila proviene del índice 2, la segunda del 0 y la tercera del 1.

Ejercicio 3: resolver con LU

Para las matrices L y U del ejemplo, verificá que b=[7,−8,18] conduce a x=[1,2,3].

Ver solución razonada

La sustitución progresiva da y=[7,−22,3]. Al resolver Ux=y desde abajo se obtiene x₃=3, x₂=2 y x₁=1.

Ejercicio 4: determinante

Si diag(U)=[2,−8,1] y no hubo intercambios, calculá det(A).

Ver solución

El producto diagonal es 2×(−8)×1=−16. Como no hubo intercambios, el signo no cambia.

Ejercicio 5: desempaquetar L y U

Implementá una función que separe una matriz LU combinada.

Ver una posible solución
function separarLU(LU) {
  const n = LU.length;
  const L = Array.from({ length: n }, () => Array(n).fill(0));
  const U = Array.from({ length: n }, () => Array(n).fill(0));

  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      if (i > j) L[i][j] = LU[i][j];
      else U[i][j] = LU[i][j];
    }
    L[i][i] = 1;
  }
  return { L, U };
}

console.log(separarLU([[2, 1], [2, -8]]));

17 · Síntesis

Ideas para recordar

  • El pivoteo intercambia filas para evitar divisiones nulas o numéricamente peligrosas.
  • El pivoteo parcial elige el mayor valor absoluto de la columna activa.
  • Una matriz o vector de permutación registra el orden de las filas.
  • Sin intercambios, la eliminación puede escribirse como A=LU.
  • Con pivoteo, la relación general es PA=LU.
  • L guarda multiplicadores y U guarda la matriz triangular superior.
  • Resolver requiere una sustitución progresiva y otra regresiva.
  • Una sola factorización sirve para muchos vectores independientes.
  • Los residuos y el error PA−LU permiten verificar la implementación.

En el próximo tema estudiaremos los métodos iterativos de Jacobi y Gauss-Seidel, especialmente útiles para sistemas grandes donde construir una factorización completa puede ser costoso.