算法背景
高斯消元法(Gaussian Elimination)是线性代数中求解线性方程组、计算矩阵秩、求逆矩阵的核心算法。由Carl Friedrich Gauss系统化提出,其基本思想是通过初等行变换将增广矩阵化为行阶梯形(前向消元),再通过回代求解未知数。该方法在计算机图形学(坐标变换)、经济学(投入产出分析)、工程计算(结构力学)等领域不可或缺。
核心思想
高斯消元分为两个阶段:
1. 前向消元:选取主元,通过行变换将主元下方元素消为0,最终得到上三角矩阵
2. 回代求解:从最后一行开始,依次代入已求得的变量值,解出当前未知数
为提升数值稳定性,实践中常采用列主元消去法(每列选取绝对值最大的元素作为主元)。
Java实现
import java.util.Arrays;
/**
* 高斯消元法实现
* 支持线性方程组求解、矩阵求逆与秩计算
*/
public class GaussianElimination {
private static final double EPS = 1e-9;
/**
* 使用高斯消元法求解线性方程组 Ax = b
* @param a 系数矩阵(n x n)
* @param b 常数向量(n)
* @return 解向量x,若无解或多解返回null
*/
public static double[] solve(double[][] a, double[] b) {
int n = a.length;
// 构造增广矩阵
double[][] aug = new double[n][n + 1];
for (int i = 0; i < n; i++) {
System.arraycopy(a[i], 0, aug[i], 0, n);
aug[i][n] = b[i];
}
// 前向消元
for (int col = 0, row = 0; col < n && row < n; col++, row++) {
// 列主元选取:在当前列下方找绝对值最大的元素
int pivotRow = row;
for (int i = row + 1; i < n; i++) {
if (Math.abs(aug[i][col]) > Math.abs(aug[pivotRow][col])) {
pivotRow = i;
}
}
// 如果主元近似为0,说明矩阵奇异
if (Math.abs(aug[pivotRow][col]) < EPS) {
return null; // 无解或无穷多解
}
// 交换当前行与主元行
double[] temp = aug[row];
aug[row] = aug[pivotRow];
aug[pivotRow] = temp;
// 将主元归一化为1(可选,不影响结果但便于观察)
double pivot = aug[row][col];
for (int j = col; j <= n; j++) {
aug[row][j] /= pivot;
}
// 消去下方所有行的当前列元素
for (int i = row + 1; i < n; i++) {
double factor = aug[i][col];
if (Math.abs(factor) < EPS) continue;
for (int j = col; j <= n; j++) {
aug[i][j] -= factor * aug[row][j];
}
}
}
// 回代求解
double[] x = new double[n];
for (int i = n - 1; i >= 0; i--) {
x[i] = aug[i][n];
for (int j = i + 1; j < n; j++) {
x[i] -= aug[i][j] * x[j];
}
// 如果系数矩阵已归一化,此处无需再除
if (Math.abs(aug[i][i]) > EPS) {
x[i] /= aug[i][i];
}
}
return x;
}
/**
* 计算矩阵的秩
*/
public static int rank(double[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
double[][] m = new double[rows][cols];
for (int i = 0; i < rows; i++) {
m[i] = matrix[i].clone();
}
int r = 0;
for (int c = 0; c < cols && r < rows; c++) {
// 找主元
int pivotRow = r;
for (int i = r + 1; i < rows; i++) {
if (Math.abs(m[i][c]) > Math.abs(m[pivotRow][c])) {
pivotRow = i;
}
}
if (Math.abs(m[pivotRow][c]) < EPS) continue;
// 交换
double[] temp = m[r];
m[r] = m[pivotRow];
m[pivotRow] = temp;
// 消元
for (int i = r + 1; i < rows; i++) {
double factor = m[i][c] / m[r][c];
for (int j = c; j < cols; j++) {
m[i][j] -= factor * m[r][j];
}
}
r++;
}
return r;
}
/**
* 计算矩阵的行列式(仅方阵)
*/
public static double determinant(double[][] matrix) {
int n = matrix.length;
double[][] m = new double[n][n];
for (int i = 0; i < n; i++) {
m[i] = matrix[i].clone();
}
double det = 1.0;
int swapCount = 0;
for (int i = 0; i < n; i++) {
// 找主元
int pivotRow = i;
for (int j = i + 1; j < n; j++) {
if (Math.abs(m[j][i]) > Math.abs(m[pivotRow][i])) {
pivotRow = j;
}
}
if (Math.abs(m[pivotRow][i]) < EPS) {
return 0; // 奇异矩阵,行列式为0
}
if (pivotRow != i) {
double[] temp = m[i];
m[i] = m[pivotRow];
m[pivotRow] = temp;
swapCount++;
}
det *= m[i][i];
// 消去下方元素
for (int j = i + 1; j < n; j++) {
double factor = m[j][i] / m[i][i];
for (int k = i; k < n; k++) {
m[j][k] -= factor * m[i][k];
}
}
}
// 行交换改变行列式符号
if (swapCount % 2 == 1) {
det = -det;
}
return det;
}
/**
* 矩阵求逆(使用增广矩阵 [A|I] 变换为 [I|A^-1])
*/
public static double[][] inverse(double[][] matrix) {
int n = matrix.length;
// 构造增广矩阵 [A | I]
double[][] aug = new double[n][2 * n];
for (int i = 0; i < n; i++) {
System.arraycopy(matrix[i], 0, aug[i], 0, n);
aug[i][n + i] = 1.0;
}
// 高斯-约旦消元
for (int i = 0; i < n; i++) {
// 列主元
int pivotRow = i;
for (int j = i + 1; j < n; j++) {
if (Math.abs(aug[j][i]) > Math.abs(aug[pivotRow][i])) {
pivotRow = j;
}
}
if (Math.abs(aug[pivotRow][i]) < EPS) {
return null; // 矩阵不可逆
}
// 交换
double[] temp = aug[i];
aug[i] = aug[pivotRow];
aug[pivotRow] = temp;
// 归一化当前行
double pivot = aug[i][i];
for (int j = 0; j < 2 * n; j++) {
aug[i][j] /= pivot;
}
// 消去其他行的当前列
for (int j = 0; j < n; j++) {
if (j == i) continue;
double factor = aug[j][i];
if (Math.abs(factor) < EPS) continue;
for (int k = 0; k < 2 * n; k++) {
aug[j][k] -= factor * aug[i][k];
}
}
}
// 提取逆矩阵
double[][] inv = new double[n][n];
for (int i = 0; i < n; i++) {
System.arraycopy(aug[i], n, inv[i], 0, n);
}
return inv;
}
/**
* 打印矩阵
*/
public static void printMatrix(double[][] matrix) {
for (double[] row : matrix) {
for (double val : row) {
System.out.printf("%10.4f ", val);
}
System.out.println();
}
}
/**
* 打印向量
*/
public static void printVector(double[] vec) {
System.out.println(Arrays.toString(vec));
}
public static void main(String[] args) {
System.out.println("=== 线性方程组求解 ===");
// 方程组:
// 2x + y - z = 8
// -3x - y + 2z = -11
// -2x + y + 2z = -3
double[][] a = {
{2, 1, -1},
{-3, -1, 2},
{-2, 1, 2}
};
double[] b = {8, -11, -3};
System.out.println("系数矩阵 A:");
printMatrix(a);
System.out.println("常数向量 b: " + Arrays.toString(b));
double[] x = solve(a, b);
if (x != null) {
System.out.println("解向量 x: " + Arrays.toString(x));
// 验证
System.out.println("验证 Ax:");
for (int i = 0; i < a.length; i++) {
double sum = 0;
for (int j = 0; j < a[0].length; j++) {
sum += a[i][j] * x[j];
}
System.out.printf(" 方程 %d: %.4f (应为 %.4f)%n", i + 1, sum, b[i]);
}
} else {
System.out.println("方程组无解或有无穷多解");
}
System.out.println("\n=== 矩阵求逆 ===");
double[][] inv = inverse(a);
if (inv != null) {
System.out.println("逆矩阵 A^-1:");
printMatrix(inv);
} else {
System.out.println("矩阵不可逆");
}
System.out.println("\n=== 行列式计算 ===");
double det = determinant(a);
System.out.printf("det(A) = %.4f%n", det);
System.out.println("\n=== 矩阵秩计算 ===");
double[][] rankMatrix = {
{1, 2, 3},
{2, 4, 6},
{1, 1, 1}
};
System.out.println("矩阵:");
printMatrix(rankMatrix);
System.out.println("秩: " + rank(rankMatrix));
System.out.println("\n=== 奇异矩阵检测 ===");
double[][] singular = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("矩阵:");
printMatrix(singular);
System.out.printf("行列式: %.4f%n", determinant(singular));
System.out.println("逆矩阵: " + (inverse(singular) == null ? "不存在" : "存在"));
}
}
复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 线性方程组求解 | $O(n^3)$ | $O(n^2)$ | 前向消元+回代 |
| 矩阵求逆 | $O(n^3)$ | $O(n^2)$ | 高斯-约旦消元 |
| 行列式计算 | $O(n^3)$ | $O(n^2)$ | 化为上三角后乘积 |
| 秩计算 | $O(\min(m,n) \cdot m \cdot n)$ | $O(m \cdot n)$ | 行阶梯形非零行数 |
算法优势与适用场景
优势:
– 通用性强,可处理任意规模的稠密线性方程组
– 列主元消去大幅提升数值稳定性
– 通过增广矩阵扩展可统一处理求逆、求秩等问题
适用场景:
– 工程计算中的结构力学与电路分析
– 计算机图形学的三维坐标变换
– 机器学习中的最小二乘线性回归
– 经济学投入产出模型求解