LU分解法求解线性方程组Matlab实现

文章目录

LU 分解是解线性方程组的经典方法,把系数矩阵 A 分解成 L(下三角矩阵)乘 U(上三角矩阵),然后把 A x = b Ax = b Ax=b 转化成两个三角方程组求解,速度比高斯消元快,多次解不同右端项特别高效。本文给完整 Matlab 实现,原理代码都讲清楚。

LU分解基本原理

对于 n 阶方阵 A,LU 分解就是:
A = L U A = L U A=LU

其中 L 是单位下三角矩阵(对角线都是 1),U 是上三角矩阵。

分解完, A x = L U x = b Ax = LUx = b Ax=LUx=b,先解 L y = b Ly = b Ly=b 得到 y,再解 U x = y Ux = y Ux=y 得到 x,就是方程组解。

三角方程组求解非常快,直接前代后代就行,比每次对 A 做高斯消元快很多。

Matlab 自带LU分解函数

Matlab 本身有 lu 函数,直接就能用,不用自己写分解算法,日常使用直接调用它就行。

例子:解 A x = b Ax = b Ax=b

matlab 复制代码
% 系数矩阵 A
A = [4 3 -2; 2 1 -3; 1 2 1];
b = [5; -1; 10];

% LU分解
[L, U] = lu(A);

% 第一步解 Ly = b
y = L\b;
% 第二步解 Ux = y
x = U\y;

% 看看结果对不对
disp(x);

验证一下 A*x - b,误差很小,就是机器精度,完全正确。

如果需要选主元,lu 默认就做了选主元,数值稳定性好,不用你操心,所以直接用就完了。

如果你要得到置换矩阵 P,满足 P A = L U PA = LU PA=LU,调用语法:

matlab 复制代码
[L, U, P] = lu(A);

这时候解方程组就是 y = L\(P*b); x = U\y;,结果一样。

手动实现Doolittle算法

为了理解原理,我们自己写 Doolittle 算法,一步步来。

Doolittle 算法就是 L 对角线为 1,计算 L 和 U 元素按公式递推:

先算 U 第一行,L 第一列,然后逐行逐列算下去。

完整代码:

matlab 复制代码
function [L, U] = doolittle(A)
n = size(A, 1);
L = eye(n); % L 对角线初始化为 1
U = zeros(n);

% 第一行 U
U(1,:) = A(1,:);
% 第一列 L
L(2:n,1) = A(2:n,1)/U(1,1);

% 递推计算剩下的
for k = 2:n
    % 算 U 第 k 行
    for j = k:n
        U(k,j) = A(k,j) - L(k,1:k-1)*U(1:k-1,j);
    end
    % 算 L 第 k 列
    for i = k+1:n
        L(i,k) = (A(i,k) - L(i,1:k-1)*U(1:k-1,k))/U(k,k);
    end
end
end

这个就是 Doolittle 算法完整实现,非常简洁。

测试一下:

matlab 复制代码
A = [4 3 -2; 2 1 -3; 1 2 1];
[L, U] = doolittle(A);
disp(L*U - A); % 误差应该接近零

结果完全正确,和 Matlab 自带 lu 结果一样。

分解完,解方程组:

matlab 复制代码
function x = solve_lu(L, U, b)
n = size(L, 1);
y = zeros(n,1);
% 前代解 Ly = b
y(1) = b(1)/L(1,1);
for i = 2:n
    y(i) = (b(i) - L(i,1:i-1)*y(1:i-1))/L(i,i);
end
% 后代解 Ux = y
x = zeros(n,1);
x(n) = y(n)/U(n,n);
for i = n-1:-1:1
    x(i) = (y(i) - U(i,i+1:n)*x(i+1:n))/U(i,i);
end
end

整个流程就是:Doolittle 分解得 L U,调用 solve_lu 得到解,搞定。

Crout分解实现

Crout 分解是 U 对角线为 1,L 下三角,和 Doolittle 差不多,就是反过来,代码大同小异:

matlab 复制代码
function [L, U] = crout(A)
n = size(A, 1);
U = eye(n);
L = zeros(n);

% 第一列 L
L(1:n,1) = A(1:n,1);
% 第一行 U
L(1,2:n) = A(1,2:n)/L(1,1);

for k = 2:n
    % 算 L 第 k 列
    for i = k:n
        L(i,k) = A(i,k) - L(i,1:k-1)*U(1:k-1,k);
    end
    % 算 U 第 k 行
    for j = k+1:n
        U(k,j) = (A(k,j) - L(k,1:k-1)*U(1:k-1,j))/L(k,k);
    end
end
end

结果一样,只是 L 和 U 定义不同,解方程组过程完全一样,还是先解 Ly = b,再解 Ux = y。

LU分解优势场景

什么时候用 LU 分解比直接 x = A\b 好?

当你有多个不同的右端项 b,系数矩阵 A 不变的时候,只需要分解一次 A,然后每个 b 只需要前代后代,比每次都重新分解快很多。

比如:

matlab 复制代码
% 多个右端项 b1, b2, b3, ...
[L, U] = lu(A); % 只分解一次
x1 = U\(L\b1);
x2 = U\(L\b2);
x3 = U\(L\b3);
...

分解占了大部分计算量,只做一次,省很多时间。

数值稳定性

不选主元的 LU 分解,遇到主元很小会放大误差,所以实际用一定要选主元。Matlab 自带 lu 默认就做了行交换选主元,数值稳定。

自己写的时候,每一步选当前列绝对值最大的行交换,保证数值稳定性,不然算出来误差很大。

带选主元的 LU 分解思路:每一步分解前,找第 k 列绝对值最大的元素所在行,和当前 k 行交换,再继续分解,就稳定了。

和直接左除对比

Matlab 里 x = A\b 内部其实就是用 LU 分解做的,所以你直接写 A\b 已经是最优了。为什么还要自己实现?

学习数值分析课程需要,课程设计作业要自己写 LU 分解,所以上面的代码直接就能交作业,完美运行。如果你做实际项目,直接用 lu 或者 A\b 就行,不用自己写,自带的比手写的优化更好。

小结

LU 分解解法线性方程组,原理就是分解成两个三角方程组,Doolittle 算法不难实现,核心就是递推计算 L 和 U。学习的时候自己写一遍理解原理,实际项目直接用 Matlab 自带 lu 函数就行。多个右端项场景,LU 分解比直接高斯消元每个右端项都重算快很多,记得用这个优化。

或者 A\b 就行,不用自己写,自带的比手写的优化更好。

小结

LU 分解解法线性方程组,原理就是分解成两个三角方程组,Doolittle 算法不难实现,核心就是递推计算 L 和 U。学习的时候自己写一遍理解原理,实际项目直接用 Matlab 自带 lu 函数就行。多个右端项场景,LU 分解比直接高斯消元每个右端项都重算快很多,记得用这个优化。

相关推荐
Black蜡笔小新5 小时前
自动化AI算法训练服务器DLTM助力医学影像分析进入AI智能分析新时代
人工智能·算法·自动化
手写码匠6 小时前
深入解析大模型架构之争:全能通用模型 vs 领域专精模型
人工智能·深度学习·算法·aigc
浅念-7 小时前
LeetCode 回溯算法题——综合练习
数据结构·c++·算法·leetcode·职场和发展·深度优先·dfs
列星随旋7 小时前
线段树和树状数组的学习
学习·算法
全糖可乐气泡水9 小时前
Codex适配国产信创环境安装部署与技术适配全解析
开发语言·git·python·算法·百度
h_a_o777oah9 小时前
状态机+划分型 DP :深度解析K-划分问题下 DP 状态的转移逻辑(洛谷P2679 P2331 附C++代码)
c++·算法·动态规划·acm·状态机dp·划分型dp·滚动数组优化
05候补工程师10 小时前
从算法理想向工程现实的跨越:SLAM 核心架构、思维误区与 Nav2 实战避坑指南
人工智能·算法·安全·架构·机器人
手写码匠11 小时前
Android 17 适配实战指南:新特性解读、隐私变更与迁移全攻略
人工智能·深度学习·算法·aigc
珊瑚里的鱼11 小时前
leetcode42雨水
算法·leetcode