1. 项目概述:当方程“无解”时,我们该怎么办?
在工程计算和数据分析的日常工作中,我们常常会遇到一种令人“头疼”的情况:你手头有一堆观测数据,想用一个数学模型去拟合它,结果列出的方程数量远远多于未知参数的个数。比如,你用十个不同时刻的传感器读数去标定一个只有三个未知参数的物理模型,这就构成了一个典型的“超定方程组”。从纯数学角度看,这个方程组没有精确解,因为你的数据点(方程)太多,而模型的自由度(未知数)太少,不可能让所有方程同时被完美满足。我第一次在信号处理中遇到这个问题时,也困惑了很久——既然无解,那我的工作岂不是进行不下去了?
当然不是。这正是“最小二乘法”大显身手的地方。它的核心思想非常直观:既然找不到一个解能让所有方程都严格成立(残差为零),那我们就退而求其次,找一个解,使得所有方程的“误差平方和”最小。这个解不是原方程的精确解,但它是所有可能解中,在最小二乘意义下“最接近”真实情况的那个,因此被称为最小二乘解。这就像用一条直线去拟合一堆散点,你找不到一条直线穿过所有点,但可以找到一条直线,使得所有点到这条直线的垂直距离的平方和最小,这条直线就是最小二乘解。
在MATLAB这个工程师的“瑞士军刀”里,求解超定方程的最小二乘解有多种内置方法,操作起来看似简单,一句x = A\b或者x = pinv(A)*b就能搞定。但你真的了解这背后的区别吗?为什么有时候推荐用左除\,有时又建议用伪逆pinv?lsqnonneg又在什么场景下使用?这些选择背后,是数值稳定性、计算效率和物理意义的多重考量。本文将从一个实践者的角度,深入拆解在MATLAB中处理超定方程的三种主流方法:左除法(\)、伪逆法(pinv)和非负最小二乘法(lsqnonneg)。我会结合具体实例,不仅告诉你“怎么用”,更重点剖析“为什么这么用”以及“不同方法间的细微差别”,最后分享一些从实际项目调试中积累的、书本上不太会写的注意事项和避坑指南。无论你是正在处理实验数据的在校学生,还是需要进行算法实现的嵌入式或算法工程师,这篇内容都能帮你更稳健、更高效地解决这个常见问题。
2. 核心概念与方案选型:三种方法的原理与抉择
在深入代码之前,我们必须先打好理论基础。理解不同方法背后的数学原理,是做出正确技术选型的前提。
2.1 超定方程与最小二乘原理
一个线性系统可以表示为Ax = b。其中,A是一个m × n的矩阵(m个方程,n个未知数),x是n × 1的未知向量,b是m × 1的观测向量。
- 恰定方程组:当 m = n 且 A 满秩时,存在唯一解。这是我们线性代数课上学的最多的情形。
- 欠定方程组:当 m < n 时,方程数少于未知数,解有无穷多个,通常需要附加条件(如最小范数解)来确定一个特解。
- 超定方程组:当 m > n 时,方程数多于未知数。除非向量 b 恰好位于矩阵 A 的列空间内,否则不存在精确解。
对于超定方程组Ax ≈ b,最小二乘解x_ls定义为使得残差向量r = b - Ax的2-范数平方(即所有残差平方和)最小的那个x:min ||Ax - b||₂²通过求导并令导数为零,我们可以得到著名的正规方程:(AᵀA)x = Aᵀb。理论上,如果A列满秩(即列向量线性无关),那么AᵀA是可逆的正定矩阵,最小二乘解可以通过x_ls = (AᵀA)⁻¹Aᵀb计算。然而,直接通过正规方程求解在实际数值计算中往往是下策,因为计算AᵀA会放大矩阵A本身的条件数(条件数平方),在A本身接近病态时,这会带来巨大的数值误差。
2.2 三种MATLAB求解方法的原理剖析
正因为直接求解正规方程不稳定,MATLAB提供了更稳健的数值方法。
2.2.1 左除法(Backslash Operator:A\b)
这是MATLAB官方最推荐、也最常用的方法。当你执行x = A\b时,MATLAB会根据矩阵A的属性自动选择最优的算法。对于超定矩形矩阵(m > n),它默认采用基于QR分解的算法。
- QR分解:将A分解为一个正交矩阵Q和一个上三角矩阵R的乘积,A = QR。由于Q是正交阵,满足QᵀQ = I,将其代入最小二乘问题:
||Ax - b||₂² = ||QRx - b||₂² = ||Rx - Qᵀb||₂² - 简化问题:因为R是上三角阵,新的最小二乘问题
||Rx - Qᵀb||₂²可以通过回代法高效、稳定地求解。整个过程避免了显式形成AᵀA,数值稳定性非常高。
注意:虽然原文提到了左除基于“奇异值分解(SVD)”,这并不完全准确。对于超定问题,
A\b的默认算法是QR分解。只有当矩阵是病态的或秩亏的,QR分解无法稳定处理时,MATLAB的算法才会退而使用SVD。因此,说左除法“最可靠”是正确的,因为它封装了智能的算法选择逻辑。
2.2.2 伪逆法(Pseudo-inverse:pinv(A)*b)
伪逆,又称Moore-Penrose逆,记为A⁺。对于任意矩阵A,伪逆是唯一的。在MATLAB中,pinv(A)函数通过奇异值分解(SVD)来计算伪逆。
- SVD分解:将A分解为A = UΣVᵀ,其中U和V是正交矩阵,Σ是对角线上为奇异值的对角矩阵。
- 构造伪逆:伪逆A⁺ = VΣ⁺Uᵀ,其中Σ⁺是将Σ中所有非零奇异值取倒数后转置得到的。
- 求解:最小二乘解可以表示为x_ls = A⁺b。伪逆法的最大优势在于它能处理秩亏矩阵(即A不是列满秩)。当A秩亏时,
AᵀA不可逆,正规方程和基于QR分解的左除法会遇到困难,但SVD可以自动识别并剔除零空间,给出一个最小范数解。其缺点是计算量通常比QR分解大,因为SVD本身是一个更复杂的计算过程。
2.2.3 非负最小二乘法(lsqnonneg(A, b))
在很多物理、化学或金融模型中,未知参数x代表的是浓度、长度、价格等,这些量具有天然的非负性。普通的最小二乘解可能包含负的分量,这在物理上是没有意义的。lsqnonneg算法就是为了求解带约束x ≥ 0的最小二乘问题。 其内部通常采用一种称为“活动集法”的迭代算法。它从一个可行解(通常是零向量)开始,逐步将变量“激活”到大于零的状态,并在每一步求解一个无约束的子问题,直到找到最优的活跃变量集合。虽然速度可能慢于前两种方法,但它保证了解的物理可实现性。
2.3 方案对比与选型指南
如何选择?这取决于你的具体问题。
| 方法 | 核心原理 | 主要优点 | 主要缺点 | 适用场景 |
|---|---|---|---|---|
左除法A\b | QR分解(主)/ SVD(备) | 计算速度快,数值稳定性高,MATLAB官方推荐。 | 对于秩亏矩阵,给出的解可能不是最小范数解(取决于算法实现)。 | 最通用、首选。适用于绝大多数列满秩的超定问题,追求计算效率时使用。 |
伪逆法pinv(A)*b | 奇异值分解(SVD) | 能稳健处理秩亏矩阵,自动给出最小范数解,概念清晰。 | 计算量比QR分解大,速度相对较慢。 | 1. 怀疑矩阵A可能列不满秩或病态严重时。 2. 需要显式获得伪逆矩阵用于其他推导时。 3. 当左除法结果不稳定或异常时。 |
非负最小二乘lsqnonneg(A,b) | 活动集法等迭代算法 | 保证解的所有分量非负,满足物理/经济约束。 | 计算速度最慢,是一个有约束优化问题。 | 所求参数必须为非负数的场景,如浓度分析、图像处理中的像素值、经济学中的价格等。 |
个人经验与选型心得:
- 默认首选
A\b:在工程实践中,只要你的模型设计合理,数据矩阵A通常是列满秩的。这时A\b是最快、最稳的选择。我90%以上的超定问题都用它。 - 把
pinv当作“安全网”:当你处理来源复杂、可能存在线性相关列的数据(例如,某些特征可能由其他特征线性组合而成),或者左除法给出了异常大的解向量时,就应该换用pinv来验证。SVD会对小奇异值进行截断,从而提供一个更稳定的解。 - 非负约束是硬需求:不要试图在得到负解后再手动置零,这会导致解不再是最优的。
lsqnonneg求解的是另一个优化问题,其结果与无约束解手动置零的结果不同。
3. 核心细节解析与实操要点
理解了原理,我们来看看在MATLAB中具体操作时,有哪些必须注意的细节。这些细节往往决定了计算的成败与精度。
3.1 数据准备与矩阵构建的陷阱
超定方程Ax = b的构建,源头在于你的A矩阵和b向量。这里出错,后面任何高级算法都无力回天。
- 维度一致性检查:这是最基础也最易犯的错误。确保A的列数等于未知数个数n,行数等于观测数据量m。b必须是一个长度为m的列向量。我习惯在计算前加一行断言:
assert(size(A,1) == length(b), ‘Dimensions of A and b do not match!’)。 - 数据标准化/归一化:如果A的各个列(代表不同特征或参数)的量纲或数量级差异巨大(例如,第一列是温度(0-100),第二列是压力(100000-200000)),直接求解会导致数值计算不稳定,且解的大小会被大数值列主导。强烈建议进行列标准化:将每一列减去其均值,除以其标准差。这不仅能提升数值稳定性,有时还能使解更容易解释。
% 列标准化示例 A_mean = mean(A); A_std = std(A); A_normalized = (A - A_mean) ./ A_std; % 注意使用点除 % 对b也可以考虑去中心化 b_mean = mean(b); b_normalized = b - b_mean; % 用标准化后的数据求解 x_normalized x_normalized = A_normalized \ b_normalized; % 重要:将解变换回原始尺度 % 对于标准化:x_original = x_normalized ./ A_std’; % 对于去中心化的b,需要调整常数项(如果模型有截距项,通常已包含在A中) - 常数项(截距)的处理:在曲线拟合(如线性拟合
y = kx + b)中,截距b也是一个待估参数。这时,你需要在矩阵A中添加一列全为1的列来对应这个常数项。
忘记添加常数项列是导致拟合系统偏差的常见原因。% 例如,用模型 y = a*x^2 + b*x + c 拟合数据 x_data = …; % 你的自变量数据 y_data = …; % 你的因变量数据 A = [x_data.^2, x_data, ones(size(x_data))]; % 注意ones那列对应常数项c b = y_data; params = A \ b; % params(1)=a, params(2)=b, params(3)=c
3.2 解的验证与残差分析
得到解x后,工作只完成了一半。验证解的合理性至关重要。
- 计算残差:残差向量
r = b - A*x直观反映了拟合的好坏。计算其2-范数norm(r)或均方根误差sqrt(mean(r.^2))。r = b - A * x; RMSE = sqrt(mean(r.^2)); % 均方根误差 fprintf(‘RMSE: %f\n’, RMSE); - 可视化残差:对于数据拟合问题,将残差
r相对于观测序号或自变量x画出来。一个理想的拟合,残差应该随机、均匀地分布在零线附近,没有明显的趋势或模式。如果残差图呈现出明显的曲线或规律,说明你的模型可能缺失了关键项(例如,该用二次模型但你用了一次线性模型)。 - 检查解的物理意义:看看解向量
x的各个分量。它们的数量级是否符合预期?符号是否正确(例如,一个表示阻尼的系数应为负)?如果出现绝对值极大或极小的值,或者符号相反,可能意味着数据存在问题、模型不合适,或者矩阵A病态。
3.3 病态问题识别与应对
“病态”是数值线性代数中的核心难题。一个病态的系统,输入数据(A,b)的微小扰动会导致解x的巨大变化。这在实际中意味着,你的测量噪声会极大地扭曲参数估计。
- 如何识别病态?
- 条件数:MATLAB中
cond(A)或cond(A’*A)可以计算条件数。条件数越大(比如 > 1e10),问题越病态。注意:计算cond(A’*A)会平方条件数,可能溢出,更推荐用cond(A)。 - 奇异值:通过
svd(A)查看奇异值。如果存在一个或多个奇异值比其他奇异值小好几个数量级,那么问题就是病态的。小奇异值对应的方向在解中会被噪声极度放大。
- 条件数:MATLAB中
- 如何应对病态?
- 正则化(岭回归):这是处理病态问题最有效的方法之一。它在损失函数中增加一个惩罚项:
min ||Ax - b||² + λ||x||²,其中 λ > 0 是正则化参数。这等价于求解修正后的正规方程(AᵀA + λI)x = Aᵀb。在MATLAB中,可以手动实现:
正则化通过牺牲一点拟合偏差,换取了解方差的大幅降低,从而获得更稳定、更可靠的解。选择合适的 λ 需要技巧,常用方法包括交叉验证或L曲线法。lambda = 1e-6; % 需要根据情况调整 n = size(A, 2); x_ridge = (A’ * A + lambda * eye(n)) \ (A’ * b); - 使用
pinv并设置容差:pinv(A, tol)允许你指定一个容差tol,所有小于tol的奇异值在求逆时被视为零。这相当于自动进行了一种截断奇异值分解(TSVD)正则化。对于病态问题,这比原始pinv(A)更稳健。 - 重新审视问题:病态的根本原因往往是模型本身或数据采集的问题。检查你的特征(A的列)是否高度相关?能否通过物理知识增加先验信息或约束?能否采集更多样化的数据来改善矩阵的条件?
- 正则化(岭回归):这是处理病态问题最有效的方法之一。它在损失函数中增加一个惩罚项:
4. 实操过程与核心环节实现
下面,我们通过一个完整的、贴近实际工程的例子,将上述所有知识点串联起来。假设我们要根据一组电压和电流的测量数据,来估计一个线性电阻电路的等效电阻R和电压源V(模型为V_measure = I * R + V_source)。
4.1 问题构建与数据模拟
首先,我们模拟一组带有噪声的实测数据。
% 1. 定义真实参数 R_true = 100; % 真实电阻,单位:欧姆 V_true = 5; % 真实电压源,单位:伏特 % 2. 生成一组电流值(自变量) I = linspace(0, 0.1, 50)’; % 从0到0.1A,生成50个电流点,列向量 % 3. 根据模型计算无噪声的理想电压 V_ideal = I * R_true + V_true; % 4. 添加高斯白噪声,模拟测量误差 noise_level = 0.1; % 噪声标准差,0.1V V_noisy = V_ideal + noise_level * randn(size(I)); % 5. 构建超定方程 Ax = b % 模型: V_measured = I * R + V_source % 因此: A = [I, 1], x = [R; V_source], b = V_noisy A = [I, ones(size(I))]; % 第一列是I,第二列是全1(对应常数项V_source) b = V_noisy; fprintf(‘数据点数量(方程数)m = %d\n’, size(A,1)); fprintf(‘未知参数个数 n = %d\n’, size(A,2)); fprintf(‘这是一个超定问题 (m > n)。\n’);4.2 应用三种方法求解并对比
现在,我们用三种方法分别求解,并比较结果。
% 方法1:左除法 (推荐首选) x_backslash = A \ b; R_backslash = x_backslash(1); V_backslash = x_backslash(2); % 方法2:伪逆法 x_pinv = pinv(A) * b; R_pinv = x_pinv(1); V_pinv = x_pinv(2); % 方法3:非负最小二乘法 (本例中电阻和电压应为正,适用) x_nonneg = lsqnonneg(A, b); R_nonneg = x_nonneg(1); V_nonneg = x_nonneg(2); % 显示结果 fprintf(‘\n——— 求解结果对比 ———\n’); fprintf(‘真实值: R = %.2f Ω, V = %.2f V\n’, R_true, V_true); fprintf(‘左除法: R = %.2f Ω, V = %.2f V\n’, R_backslash, V_backslash); fprintf(‘伪逆法: R = %.2f Ω, V = %.2f V\n’, R_pinv, V_pinv); fprintf(‘非负最小二乘: R = %.2f Ω, V = %.2f V\n’, R_nonneg, V_nonneg);运行这段代码,你会看到三种方法给出的结果非常接近真实值,且彼此差异很小。这是因为我们模拟的问题是一个良态问题。
4.3 结果验证与残差分析
接下来,我们进行严谨的验证。
% 计算残差和RMSE residual_backslash = b - A * x_backslash; RMSE_backslash = sqrt(mean(residual_backslash.^2)); residual_pinv = b - A * x_pinv; RMSE_pinv = sqrt(mean(residual_pinv.^2)); residual_nonneg = b - A * x_nonneg; RMSE_nonneg = sqrt(mean(residual_nonneg.^2)); fprintf(‘\n——— 拟合优度对比 ———\n’); fprintf(‘左除法 RMSE: %.4f V\n’, RMSE_backslash); fprintf(‘伪逆法 RMSE: %.4f V\n’, RMSE_pinv); fprintf(‘非负最小二乘 RMSE: %.4f V\n’, RMSE_nonneg); % 可视化:原始数据、拟合曲线和残差 figure(‘Position’, [100, 100, 1200, 400]); % 子图1:数据与拟合曲线 subplot(1, 2, 1); scatter(I, V_noisy, 40, ‘b’, ‘.’); hold on; plot(I, A * x_backslash, ‘r-‘, ‘LineWidth’, 2); plot(I, A * x_pinv, ‘g–‘, ‘LineWidth’, 1.5); plot(I, A * x_nonneg, ‘m:’, ‘LineWidth’, 1.5); xlabel(‘电流 I (A)’); ylabel(‘电压 V (V)’); title(‘测量数据与拟合曲线’); legend(‘带噪声数据’, ‘左除法拟合’, ‘伪逆法拟合’, ‘非负最小二乘拟合’, ‘Location’, ‘northwest’); grid on; % 子图2:残差分布 subplot(1, 2, 2); plot(I, residual_backslash, ‘ro-‘, ‘MarkerSize’, 4); hold on; plot(I, residual_pinv, ‘g^–‘, ‘MarkerSize’, 4); plot(I, residual_nonneg, ‘ms:’, ‘MarkerSize’, 4); plot([min(I), max(I)], [0, 0], ‘k-‘, ‘LineWidth’, 1); % 零线 xlabel(‘电流 I (A)’); ylabel(‘残差 (V)’); title(‘残差分布图’); legend(‘左除法残差’, ‘伪逆法残差’, ‘非负最小二乘残差’, ‘Location’, ‘best’); grid on;通过残差图,我们可以直观地看到误差是否随机分布。在这个理想例子中,残差应该均匀分布在零线上下。
4.4 引入病态场景与正则化演示
为了展示病态问题和正则化的作用,我们故意构造一个特征高度相关的矩阵A。
% 构造一个病态问题:拟合 y = a*x1 + b*x2,但x2 ≈ x1 + 微小噪声 x1 = randn(100, 1); x2 = x1 + 1e-3 * randn(100, 1); % x2与x1高度相关 y = 2*x1 + 3*x2 + 0.1*randn(100,1); % 真实模型加噪声 A_ill = [x1, x2]; cond_A = cond(A_ill); fprintf(‘\n构造的病态矩阵条件数 cond(A) = %.2e (非常大)\n’, cond_A); % 普通最小二乘(左除) x_ls = A_ill \ y; fprintf(‘普通最小二乘解: a = %.6f, b = %.6f\n’, x_ls(1), x_ls(2)); % 注意:由于病态,解可能非常不稳定,且与真实值(2,3)相差甚远。 % 岭回归正则化 lambda = 0.1; % 正则化参数,需要调整 n = size(A_ill, 2); x_ridge = (A_ill’ * A_ill + lambda * eye(n)) \ (A_ill’ * y); fprintf(‘岭回归解 (λ=%.2f): a = %.6f, b = %.6f\n’, lambda, x_ridge(1), x_ridge(2)); % 使用带容差的pinv(截断SVD) tol = 1e-4; x_pinv_tol = pinv(A_ill, tol) * y; fprintf(‘带容差的pinv解 (tol=%.0e): a = %.6f, b = %.6f\n’, tol, x_pinv_tol(1), x_pinv_tol(2));运行这个例子,你会观察到普通最小二乘解x_ls可能变得异常巨大且不稳定,而岭回归和截断SVD得到的解虽然也有偏差,但数值范围更合理、更稳定。这生动地展示了处理病态问题的必要性。
5. 常见问题与排查技巧实录
在实际项目中,你绝不会总是一帆风顺。下面是我在多年工作中总结的一些典型问题及其解决方法。
5.1 维度错误与矩阵构建错误
- 问题:执行
A\b时,MATLAB报错“矩阵维度必须一致”或“内部维度不一致”。 - 排查:
- 检查大小:立刻在命令行输入
size(A)和size(b)。确保size(A,1) == size(b,1)。b必须是列向量,如果是行向量,使用b = b(:)或b = b’进行转置。 - 检查模型:回顾你的数学模型。例如,进行二次多项式拟合
y = ax² + bx + c时,矩阵A应为[x.^2, x, ones(size(x))]。漏掉ones列是常见错误。
- 检查大小:立刻在命令行输入
- 技巧:在编写构建矩阵A的代码时,使用明确的列连接,并添加注释。
% 正确示例:二次拟合 A = [x_data.^2, x_data, ones(size(x_data))]; % 三列分别对应 a, b, c % 而不是容易出错的: % A = [x_data.^2, x_data, 1]; % 错误!1需要扩展为与x_data同长的向量
5.2 解出 NaN 或 Inf
- 问题:求解后得到的
x向量中包含NaN(非数字)或Inf(无穷大)。 - 原因与解决:
- 矩阵奇异或秩亏:
AᵀA不可逆。这可能是因为A中存在全零列,或者某些列是其他列的精确线性组合。- 检查数据:使用
rank(A)查看矩阵的秩。如果秩小于列数n,则是秩亏问题。 - 使用
pinv:换用x = pinv(A)*b。伪逆能处理秩亏问题,给出最小范数解。 - 检查特征:从物理或业务逻辑上,检查你输入的特征(A的列)是否有多余或重复。
- 检查数据:使用
- 数据包含 NaN 或 Inf:原始数据
A或b中本身就有缺失或无穷值。- 清理数据:使用
isnan()和isinf()函数定位并处理异常数据点,例如删除或插补。
% 查找并删除包含NaN或Inf的行 bad_rows = any(isnan(A), 2) | any(isinf(A), 2) | isnan(b) | isinf(b); A(bad_rows, :) = []; b(bad_rows) = []; - 清理数据:使用
- 矩阵奇异或秩亏:
5.3 结果不符合物理意义或常识
- 问题:解算出的参数符号错误(例如阻尼系数为正)、数量级离谱(例如电阻值为1e10欧姆)。
- 排查:
- 量纲与数量级:首先检查输入数据
A和b的量纲。如果各列数量级差异巨大,务必先进行标准化(见3.1节)。 - 模型错误:这是最根本的原因。你的数学模型
Ax = b是否准确描述了物理过程?是否遗漏了关键项?用残差图(见4.3节)可以帮助诊断。如果残差呈现明显的趋势,说明模型不完善。 - 病态问题:即使模型正确,病态也会放大噪声,导致解失真。计算
cond(A),如果非常大(如>1e8),就需要考虑正则化或使用pinv。 - 过拟合:如果参数太多(
n太大)而数据点(m)相对不足,模型会去拟合噪声而不是趋势。确保m远大于n(例如 m > 5n 或 10n 是较好的经验法则)。对于复杂模型,考虑使用正则化来抑制过拟合。
- 量纲与数量级:首先检查输入数据
5.4 性能优化:当数据量巨大时
当矩阵A非常庞大(行数m上万甚至百万)时,直接求解可能内存不足或速度很慢。
- 使用稀疏矩阵:如果A中大部分元素是0,务必使用
sparse类型存储。MATLAB对稀疏矩阵的运算有极大优化。A_sparse = sparse(A); x = A_sparse \ b; % MATLAB会自动选择适用于稀疏矩阵的算法 - 迭代法:对于极其庞大的问题,直接法(如QR、SVD)可能不可行。可以考虑迭代法,如共轭梯度法(CG)用于对称正定系统,或LSQR算法专门用于最小二乘问题。MATLAB提供了
lsqr函数。
迭代法不需要显式存储整个矩阵,适合处理流式数据或分布式计算。x_iterative = lsqr(A, b, 1e-6, 100); % 设置容差和最大迭代次数
5.5 一个综合排查清单
当你得到不满意的结果时,可以按以下清单逐步排查:
- 数据检查:
A和b中有 NaN/Inf 吗?用any(isnan(A(:)))检查。 - 维度检查:
size(A)和size(b)匹配吗?b是列向量吗? - 可视化数据:画出
b相对于A各列的散点图,看看关系是否大致线性。 - 计算矩阵条件数:
cond(A)是否过大?(>1e10 需警惕) - 计算矩阵的秩:
rank(A)是否等于列数n?如果小于,是秩亏问题。 - 尝试不同解法:比较
A\b、pinv(A)*b和pinv(A, tol)*b(设置一个合理的tol,如1e-6)的结果。如果差异巨大,病态可能性高。 - 分析残差:计算并绘制残差图。随机分布吗?有规律吗?
- 简化问题:如果可能,用一小部分已知结果的数据进行测试,看算法是否能复现已知解。
最后,记住一点:最小二乘求解是一个工具,但它无法弥补糟糕的数据或错误的模型。在调用A\b之前,花在数据清洗、探索性分析和模型思考上的时间,往往比选择哪种求解算法更重要。理解你的数据,理解你的模型,然后让MATLAB这个强大的计算引擎为你提供那个在最小二乘意义上“最优”的答案。