线性方程组求解中高斯/追赶法异常大误差问题排查
问题排查与修复方案
核心错误分析
你的代码存在三个关键问题,直接导致批量测试时出现异常大误差:
1. 矩阵生成函数未正确替换奇异矩阵
generate_random_matrix和tridiagonal_matrix中,当生成的矩阵行列式接近0(奇异矩阵)时,递归调用自身但未将新矩阵赋值给原变量,最终返回的还是初始的奇异矩阵。奇异矩阵无法唯一求解方程组,会引发消去过程中除以0或数值溢出,产生极大误差。
2. Thomas方法的真实解与求解方程组不匹配
测试Thomas方法时,你用随机矩阵A_random的解作为真实解,但Thomas实际求解的是三对角矩阵A_tridiagonal对应的方程组——这是完全不同的两个线性系统,计算出的误差毫无意义,必然出现大量异常值。
3. RMSE计算不符合标准定义
你的RMSE计算是np.sqrt(np.sum((true - computed)**2))/n,但标准RMSE应为平方误差平均值的平方根,即np.sqrt(np.mean((true - computed)**2))。这虽不是异常值的核心原因,但会导致误差数值解读偏差。
修复后的完整代码
import numpy as np import matplotlib.pyplot as plt def calculate_relative_error(true_solution, computed_solution): n = len(true_solution) # 修正RMSE为标准定义 rmse = np.sqrt(np.mean((true_solution - computed_solution)**2)) sup_norm = np.max(np.abs(true_solution - computed_solution)) return rmse, sup_norm def generate_random_matrix(n = 6 ): A = (np.random.random((n, n)).astype(np.float64) * 2 - 1).astype(np.float64) # 用循环替换递归,避免栈溢出,同时用阈值判断奇异矩阵 while np.linalg.det(A) < 1e-10: A = (np.random.random((n, n)).astype(np.float64) * 2 - 1).astype(np.float64) return A def tridiagonal_matrix(n): main_diag = (np.random.random(n).astype(np.float64) * 2 - 1).astype(np.float64) sub_diag = (np.random.random(n-1).astype(np.float64) * 2 - 1).astype(np.float64) super_diag = (np.random.random(n-1).astype(np.float64) * 2 - 1).astype(np.float64) A = np.diag(main_diag) + np.diag(sub_diag, k=-1) + np.diag(super_diag, k=1) # 循环生成非奇异三对角矩阵 while np.linalg.det(A) < 1e-10: main_diag = (np.random.random(n).astype(np.float64) * 2 - 1).astype(np.float64) sub_diag = (np.random.random(n-1).astype(np.float64) * 2 - 1).astype(np.float64) super_diag = (np.random.random(n-1).astype(np.float64) * 2 - 1).astype(np.float64) A = np.diag(main_diag) + np.diag(sub_diag, k=-1) + np.diag(super_diag, k=1) return A def gauss(A, b, pivoting): n = len(b) a = np.hstack((A, b[:, np.newaxis])).astype(np.float64) for i in range(n): if pivoting: max_index = np.argmax(np.abs(a[i:, i])) + i a[[i, max_index]] = a[[max_index, i]] # 添加极小值判断,避免除以0导致数值溢出 if np.abs(a[i, i]) < 1e-15: raise ValueError("Singular matrix encountered in Gaussian elimination") for j in range(i + 1, n): factor = a[j, i] / a[i, i] a[j, i:] -= factor * a[i, i:] x = np.zeros(n, dtype=np.float64) for i in range(n - 1, -1, -1): x[i] = (a[i, -1] - np.dot(a[i, i + 1:-1], x[i + 1:])) / a[i, i] return x def thomas(A, b): gamma = [-A[0][1] / A[0][0]] beta = [b[0] / A[0][0]] n = len(b) x = np.zeros(n, dtype=np.float64) # 改用numpy数组提升效率 for i in range(1, n): denominator = A[i][i - 1] * gamma[i - 1] + A[i][i] # 避免分母过小导致数值不稳定 if np.abs(denominator) < 1e-15: raise ValueError("Singular tridiagonal matrix encountered") if i != n - 1: gamma.append(-A[i][i + 1] / denominator) beta.append((b[i] - A[i][i - 1] * beta[i - 1]) / denominator) x[n - 1] = beta[n - 1] for i in range(n - 2, -1, -1): x[i] = gamma[i] * x[i + 1] + beta[i] return x num_matrices = 1000 methods = ["gauss_no_pivoting", "thomas"] for method in methods: errors_rmse = [] errors_sup_norm = [] for _ in range(num_matrices): b = np.array([1, 1, 1, 1, 1, 1]).astype(np.float64) computed_solution = None true_solution = None if method == "gauss_no_pivoting": A_random = generate_random_matrix(6) true_solution = gauss(A_random, b, pivoting=True) computed_solution = gauss(A_random.copy(), b.copy(), pivoting=False) elif method == "thomas": A_tridiagonal = tridiagonal_matrix(6) true_solution = gauss(A_tridiagonal, b, pivoting=True) # 修正真实解对应正确矩阵 computed_solution = thomas(A_tridiagonal.copy(), b.copy()) rmse, sup_norm = calculate_relative_error(true_solution, computed_solution) errors_rmse.append(rmse) errors_sup_norm.append(sup_norm) plt.figure(figsize=(10, 5)) plt.subplot(1, 2, 1) plt.hist(errors_rmse, bins=20, color='blue', edgecolor='black') plt.title(f'{method} - RMSE Histogram') plt.xlabel('RMSE') plt.ylabel('Frequency') plt.subplot(1, 2, 2) plt.hist(errors_sup_norm, bins=20, color='green', edgecolor='black') plt.title(f'{method} - Sup Norm Histogram') plt.xlabel('Sup Norm') plt.ylabel('Frequency') plt.tight_layout() plt.show()
额外优化说明
- 替换递归为循环:矩阵生成用循环代替递归,避免栈溢出且效率更高。
- 浮点精度兼容:用极小阈值(如
1e-10)判断奇异矩阵,避免浮点计算中严格等于0的误判。 - 数值稳定性防护:在消去算法中添加分母极小值判断,提前抛出异常避免数值溢出。
- 数据结构优化:Thomas方法中
x改用numpy数组,提升计算效率和一致性。
修复后,批量测试的误差会呈现正态分布,异常大误差的情况将被消除。
内容的提问来源于stack exchange,提问作者Elena
相关产品推荐
相关产品推荐

