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 分解比直接高斯消元每个右端项都重算快很多,记得用这个优化。

相关推荐
笨笨饿2 小时前
69_如何给自己手搓一个串口
linux·c语言·网络·单片机·嵌入式硬件·算法·个人开发
纽扣6672 小时前
【算法进阶之路】链表进阶:删除、合并、回文与排序全解析
数据结构·算法·链表
消失的旧时光-19433 小时前
统一并发模型:线程、Reactor、协程本质是一件事(从线程到协程 · 第6篇·终章)
java·python·算法
智者知已应修善业3 小时前
【51单片机不用数组动态数码管显示字符和LED流水灯】2023-10-3
c++·经验分享·笔记·算法·51单片机
MATLAB代码顾问4 小时前
改进遗传算法(IGA)求解作业车间调度问题(JSSP)——附MATLAB代码
开发语言·matlab
AI进化营-智能译站4 小时前
ROS2 C++开发系列16-智能指针管理传感器句柄|告别ROS2节点内存泄漏与野指针
java·c++·算法·ai
米饭不加菜4 小时前
机器人矩阵运算MATLAB计算
matlab·矩阵·机器人
CS创新实验室4 小时前
从盘边到芯端——硬盘接口七十年变迁史
算法·磁盘调度
机器学习之心4 小时前
多智能体遗传算法(MAGA)优化最优投影方向的投影寻踪聚类评价,MATLAB代码
matlab·聚类·投影寻踪聚类评价