优化|数学软件是如何求解线性方程Ax=b ?

news2024/11/20 13:33:06

在这里插入图片描述

编者按

对于大家来说,我们从学会多项式开始就得和求解矩阵方程打交道。大学之前靠手算,到了大学阶段我们学会了使用科学计算软件,然后只需要输入简单的一行指令 x = A \ b x = A \backslash b x=A\b,就可以轻轻松松求解方程组 A x = b . Ax = b. Ax=b.那么它在优化算法中有什么样的作用呢?实际应用时候又会出现什么样的问题呢?

优化和线性系统求解实例

优化问题和求解线性系统是紧密相关的。对于如下的优化问题,
min ⁡ 1 2 x ⊤ P x + q ⊤ x s . t . G x = c , \begin{align} \begin{aligned} \min & \frac{1}{2}x^\top P x + q^\top x \\ s.t. & Gx = c \end{aligned}, \end{align} mins.t.21xPx+qxGx=c,

的KKT条件就是
[ P G ⊤ G 0 ] [ x y ] = [ − q c ] . \begin{align} \begin{aligned} \begin{bmatrix} P & G^\top\\ G & 0 \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix} = \begin{bmatrix} -q \\ c \end{bmatrix}. \end{aligned} \end{align} [PGG0][xy]=[qc].

对于二次规划中常用的算子分裂法[1],[2]和内点法[3],算法的每一个周期其实也是需要求解如下的线性系统
A : = [ P G ⊤ G − D ] [ x y ] = [ r 1 r 2 ] , \begin{align} \begin{aligned} A :=\begin{bmatrix} P & G^\top\\ G & -D \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix} = \begin{bmatrix} r_1 \\ r_2 \end{bmatrix}, \end{aligned} \end{align} A:=[PGGD][xy]=[r1r2],

而且求解线性系统往往是算法中最耗时的一部分,决定了一个优化算法的最终收敛速度。

线性系统求解方法

在数学上求解线性系统是很直观的,因为 x = A − 1 b x = A^{-1}b x=A1b,那么我们计算逆矩阵 A − 1 A^{-1} A1然后乘上b似乎就解决了。这主要会带来几方面问题:

(1)求解A矩阵的逆将会很费时;

(2)即使 A A A是稀疏矩阵,它的逆矩阵也往往会丧失稀疏性,需要更多的内存来进行存储;

(3)当矩阵 A A A的条件数很差(ill-conditioned)时,这种方式的求解很可能会带来很大的误差。

求解线性系统 A x = b Ax=b Ax=b一般分为两种方法。一种是直接法,先对矩阵 M M M进行分解(factorization),然后通过分解矩阵进行反向求解(back-solve)。另外一种是迭代法,通过不断的矩阵乘法和加减法得到最后的解。

基于矩阵分解的直接法

针对矩阵 A A A的特殊结构,代码底层会调用不同的矩阵分解方法。Julia的LinearAlgebra模块自动调用方法如下[4]:

我们在这复现一段相关的Julia代码:

using LinearAlgebra
  
n = 10
m = 8
P = rand(n, n)
P = P' * P + 0.1*I
G = sprand(m,n,0.2)
d = rand(m)
d[1:3] .= 1e9
d[6:end] .= 1e-9   
A = [P G'; G -diagm(0=>d)]        #生成矩阵  A = [P G'; G -D] 
A = A + A'
R = factorize(A)         #ldlt分解
typeof(R)                

返回的结果将为

BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}}

可见对于正定对称矩阵,此时factorize()函数自动选择了BunchKaufman LDL分解。此时求解x的值可以调用 R \ b R\backslash b R\b得到,得到的效果和 A \ b A\backslash b A\b是一样的。

另外矩阵的稀疏性也会决定矩阵方法的使用。如果运行如下代码

using SparseArrays

R = factorize(sparse(A))         #ldlt分解
typeof(R)    

此时会采用稀疏形式的LDL分解,并且调用的是不同的SuiteSparse包。

SuiteSparse.CHOLMOD.Factor{Float64}
type:    LDLt
method:  simplicial
maxnnz:  80
nnz:     80
success: true

感兴趣的同学也可以参考matlab文档 (Algorithms部分)了解分解方法选取的逻辑图,https://uk.mathworks.com/help/matlab/ref/mldivide.html#bt4jslc-6。

现在最常用的求解线性系统的包应该还是LAPACK(配合BLAS进行矩阵乘法运算),但是LAPACK本身是用Fortran语言写的。所幸的是,LAPACK现在基本都有在不同语言下的接口。感兴趣的同学可以参考不同语言下相关的调用方式

•Python: https://docs.scipy.org/doc/scipy/reference/linalg.lapack.html

•Julia:https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#LinearAlgebra.LAPACK

直接法的优势是数值性能很稳定,然后一个矩阵一旦完成分解后,可以重复高效的利用矩阵分解进行计算,如SCS[1],OSQP[2]的底层算法。

基于迭代的间接法

间接法是另外一大类常用的矩阵求解算法。跟基于矩阵分解的直接法一样,也有很多基于矩阵 A A A的结构对应设计的间接求解线性系统的方法,下图截取自Julia的IterativeSolvers.jl的包:

稀疏性

在实际使用中,我们也需要考虑矩阵的稀疏性,如果矩阵本来是稀疏的,但是没有设置成稀疏类型,程序底层也会自动选取非稀疏形式的矩阵分解。当运行如下代码时:

 using LinearAlgebra, SparseArrays
 using BenchmarkTools        #记录时间的包
 
 A = Symmetric(sprandn(1000,1000,0.01))        #1000 x 1000 矩阵,稀疏性为0.1~0.2
 A = A + 0.1*I        #避免不满秩
 Ad = Matrix(A)
 @assert issymmtric(Ad)     #确保矩阵是对称的
 
 typeof(A)                  # Symmetric{Float64, SparseMatrixCSC{Float64, Int64}}
 
 @btime factorize(A)        #  28.833 ms (74 allocations: 6.58 MiB)
                            # SuiteSparse.CHOLMOD.Factor{Float64}
                            # type:    LDLt
                            # method:  simplicial
                            # maxnnz:  191629
                            # nnz:     140279
                            # success: true
 
 typeof(Ad)                  # Matrix{Float64}
 
 @btime factorize(Ad)        # 60.418 ms (8 allocations: 15.76 MiB)
                             # BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}}

可以看到上述代码中,当使用稀疏矩阵 A A A时,矩阵分解时间约为28.833ms,但是如果使用非稀疏矩阵类型, A d A_d Ad的矩阵分解时间就变成了60.418ms。

**具体选取稀疏矩阵类型还是非稀疏类型取决于实际矩阵 A A A的稀疏性。**如果我们将上述代码矩阵稀疏性提高10倍,则有如下结果:

A = Symmetric(sprandn(1000,1000,0.1))        # A的稀疏性变成之前的10倍
 A = A + 0.1*I        #避免不满秩
 Ad = Matrix(A)
 @assert issymmtric(Ad)     #确保矩阵是对称的
 
 @btime factorize(A)        #   161.359 ms (74 allocations: 25.00 MiB)
                            # SuiteSparse.CHOLMOD.Factor{Float64}
                            # type:    LDLt
                            # method:  simplicial
                            # maxnnz:  473377
                            # nnz:     431513
                            # success: true
 
 @btime factorize(Ad)        # 62.464 ms (8 allocations: 15.76 MiB)
                             # BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}}

可以看到,非稀疏矩阵分解时间几乎不变,而稀疏矩阵分解的时间反而远大于非稀疏矩阵分解的时间。

优化问题中如何求解系统(3)

这时候,如果我们回过头去看(3)中的矩阵 A A A,它本身是quasi-definite的,并且大部分实际问题下都是稀疏的,这样就非常适合使用LDL分解法[5]。许多求解器的底层都是默认使用这个矩阵分解方法。

数值实验

最后,我们对于公式(3)中定义的矩阵进行了数值实验(Julia源码),为了能有更直观的对比效果,我们人为的生成了一个条件数很差的矩阵 A A A

using LinearAlgebra, SparseArrays

n = 10
m = 8
P = rand(n, n)
P = P' * P
V, D = eigen(P)
D[1:5] .= 1e-6
D[6:end] .= 1e6
P = V * D * V'
G = sprand(m,n,0.2)
d = rand(m)
d[1:3] .= 1e9
d[6:end] .= 1e-9   
A = [P G'; G -diagm(0=>d)]        #生成矩阵  A = [P G'; G -D] 
A = A + A'
cond(A)                        # 返回条件数(Condition Number)= 1.0000000013286812e18

#ldlt分解
F = ldlt(sparse(A))
invA = inv(A)
x = zeros(m+n)
x[10] = 1e-5
x[15] = 1e5
b = A*x
x1 = invA*b
x2 = F\b

#Ax=b系统误差
res1 = norm(A*x1-b,Inf)        # res1 = 4.304065664004539e-7 
res2 = norm(A*x2-b,Inf)        # res2 = 2.9103830456733704e-11
nnz(sparse(A))                 #A中的非零项有136个
nnz(sparse(invA))              ##invA中的非零项有290个 (很接近18x18=324)

实验结果表明,当 A A A条件数很大时(例如 > 1 e 18 >1e^{18} >1e18),通过直接的逆求解得到的误差将会比用矩阵分解法得到的大很多,并且 A − 1 A^{-1} A1矩阵中非零项接近于矩阵的维度本身,丧失了 A A A潜在的稀疏性。

对于同样的问题,我们再利用迭代法GMRES求解同样的方程:

using IterativeSolvers

x3 = gmres(A,b)
res3 = norm(A*x3-b,Inf)        # res3 = 9.188644068589468e-5

可以发现对于条件数很差的矩阵,如果不做任何处理,那么迭代法最终求解的精度将很差。改善迭代法收敛精度可以通过从一个很好的初始值进行迭代来进行改善:

x4 = deepcopy(x1)        #拷贝已知的x1 = A^{-1}b的值作为迭代法初始值,此时误差为4.304065664004539e-7 
gmres!(x4,A,b)
res4 = norm(A*x4-b,Inf)        # res4 = 8.273806188044642e-14    

可以看到,如果迭代法具有很好的初始值,那么收敛精度也是可以很高的!

另外一种改善迭代法精度的方法是采用preconditioning方法改善 A A A矩阵的条件数,使得迭代法能有更好的收敛结果,在这儿就不做详细论述了。此外,对于不同矩阵结构迭代法的选取(以及preconditioning),感兴趣的同学可以参考matlab文档https://uk.mathworks.com/help/matlab/math/iterative-methods-for-linear-systems.html。

两种方法的优缺点

那这两种方法各有什么优缺点呢?看看ChatGPT给出的回答:

下面是每种方法的优缺点:

直接法:
优点:
•可以保证唯一解(如果存在)
•对于小到中等规模的线性方程组可以非常高效
•通常只需要固定数量的运算就可以计算出解​
缺点:​
•对于大型线性方程组来说,可能会非常计算密集,特别是对于稠密矩阵来说​
•舍入误差可能会累积,影响解的精度​
•对于病态矩阵来说,无法处理得很好​

间接法:​
优点:​
•可以高效地处理大型和稀疏的线性方程组​
•如果矩阵病态或奇异,可能比直接法更精确​
•可以轻松地并行化以利用多个处理器​
缺点:​
•可能需要更多的迭代才能收敛,这可能会非常计算密集​
•对于某些矩阵或初始猜测,可能无法收敛​
•收敛速度可能取决于矩阵的特性,对于某些矩阵可能会很慢​

总的来说,选择直接法还是间接法取决于线性系统的特性、问题的规模以及所需的精度级别。在实践中,可以结合使用直接法和间接法来高效而精确地求解线性方程组。

嗯…的确是这么回事。

PS: 一般来说只要不是超大规模矩阵,大家平时采用基于矩阵分解的直接法就足够了。

[1] O’Donoghue, B., Chu, E., Parikh, N., Boyd, S.: Conic optimization via operator splitting and homogeneous self-dual embedding. J. Optim. Theory Appl. 169(3), 1042–1068 (2016)
[2] B. Stellato, G. Banjac, P. Goulart, A. Bemporad, and S. Boyd, “OSQP: an operator splitting solver for
quadratic programs,” Mathematical Programming Computation, vol. 12, no. 4, pp. 637–672, 2020.
[3] S. J. Wright, Primal-Dual Interior-Point Methods. Society for Industrial and Applied Mathematics, 1997.
[4] https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#man-linalg-factorizations
[5] Vanderbei, R.: Symmetric quasi-definite matrices. SIAM J. Optim. 5(1), 100–113 (1995)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/568708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html实现酷炫科技风大屏风格模板(附源码)

文章目录 1.设计来源1.1 大屏主界面1.2 弹框界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/130870963 html实现酷炫科技风大屏风格模板源码 ,html大屏源码…

docker容器postgres数据导出命令及还原数据命令

参考资料 docker控制postgers容器导入导出数据_docker 导出数据库_奔跑的痕迹的博客-CSDN博客 --进入容器 docker exec -it 容器名称或容器id /bin/bash 以下命令是在进入容器执行的 --导出单张表的备份语句(copy模式) pg_dump -h 127.0.0.1 -U …

授权管理再工控安全中起到什么作用?

随着计算机技术、通信技术和控制技术的发展,工业自动化控制已经开始向网络化方向发展,从最初的CCS(计算机集中控制系统),到第二代的DCS(分散控制系统),发展到现在流行的FCS&#xff…

力扣 1775.通过最少操作次数使数的和相等、1014.最佳观光组合、33.搜索旋转排序数组

算法总结 最近作者在坚持每日一道中等难度算法题,也是作者考核时经常会碰到的难度,由于经常是到22:30才意识到自己并没有写算法,因此都是打开LeetCode网站随机一题,并未系统性的去学习,这一点值得反思。在做题过程中经…

航天科技AIRIOT物联会【智慧物联主题沙龙】在沈阳举办

2023年5月24日,由航天科技控股集团股份有限公司(简称“航天科技”)智慧物联事业部主办的《AIRIOT物联会-智慧物联主题分享沙龙》在沈阳举办,此次会议邀请到来自光伏、燃气、能源、水务、园区、工厂等行业的众多企业代表参加&#…

0起步用GPT+知乎赚了点小钱,人人可复制

大家好,我是五竹。 前段时间分享了一篇关于用ChatGPT赚点小钱的实战:TMD,被人偷窥了一个月!结果上周末的时候在知乎追了一个关于Claude的热点,发布了一篇注册Claude的文章,结果小小的“爆了”一下&#xf…

Qt文件系统源码分析—第五篇QTemporaryFile

深度 本文主要分析Windows平台,Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次,再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

如何查看一个 docker 镜像有哪些版本

如何查看一个 docker 镜像有哪些版本 因为通过 docker search 并不能查看某个镜像的版本信息,如我需要特定版本的 redis 那怎么办呢~ 本文提供了如下几种方式,大家可以分别逐个尝试下~ 为什么有几种方式呢,因为官方的查找镜像网址 Docker H…

使用audition测试USBaudio数据回传延时

一,简介 本文主要介绍如何使用Audition软件来测试STM32 USB audio上行音频数据的延时。 二,准备工作 Audition,ASIO驱动,STM32枚举的USB Audio高速声卡测试板。 二,硬件连接 将STM32的IIS的data in和data out使用…

四款AI视频翻译产品横评

本文内容节选自 Paxi.ai 的文章分享,从其中摘录了我觉得有意思的一部分。Paxi.ai 是一个基于 GPT-4 打造的帮用户快速使用AI的AI工具,通过与它的小助手对话可以了解各种AI的产品功能和使用方式。对本文内容感兴趣的朋友可以上他们官网查看。 有没有想过把…

go embed 实现gin + vue静态资源嵌入

前言 golang1.16出来以后,早就有打算把ckman项目的前端代码打包更换成embed。在此之前,使用的是pkger进行的打包。 但是今天打包时却报了个错: 而且通过各种手段尝试均无果之后,果断把决定立即将其更换为embed进行资源嵌入管理。…

华为OD机试真题 Java 实现【寻找符合要求的最长子串】【2023Q1 200分】

一、题目描述 给定一个字符串 s ,找出这样一个子串: 该子串中的任意一个字符最多出现2次;该子串不包含指定某个字符; 请你找出满足该条件的最长子串的长度。 二、输入描述 第一行为要求不包含的指定字符,为单个字…

一个神奇的工具,让URL地址都变成了“ooooooooo“

一个神奇的工具,让URL地址都变成了"ooooooooo" 一、核心代码二、URL编码/解码 最近发现一个有意思工具,就是将一个URL地址转换为都是 ooooooooo 的样子,通过转换后的地址访问可以转换回到原始地址,转换的逻辑有点像短链…

mysql8绿色版安装教程

最近在安装mysql8绿色版,以下是安装过程中的一些步骤: 1:解压zip压缩包至想要安装的目录,比如解压到D:\mysql\mysql-8.0.29-winx64 2:在解压目录D:\mysql-8.0.29-winx64中创建MySQL配置文件my.ini 配置文件my.ini内容…

Unity3D安装:离线安装 Unity

推荐:将 NSDT场景编辑器 加入你的3D工具链 3D工具集: NSDT简石数字孪生 在没有 Hub 的情况下离线安装 Unity Unity 下载助手 (Download Assistant) 支持离线部署。在这种部署方式中,可下载用于安装 Unity 的所有文件,然后生成脚本…

【日常】如何增加粉丝的粘性?

【日常】如何增加粉丝的粘性? 1、前言2、官方活动3、作品质量4、打造自己的社区 1、前言 不知不觉间,铁粉已经过千了,粉丝数也即将两万,总而言之,越努力,越幸运,付出就有回报(当然在…

面了个阿里拿36K出来的,真是砂纸擦屁股,给我漏了一手

今年的春招已经结束,很多小伙伴收获不错,拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文,为此咱这里也统一做一次大整理和大归类,这也算是划重点了。 俗话说得好,他山之石…

减肥瘦身自律APP软件开发功能有哪些?

减肥瘦身是很多女人一生都在奋斗的目标,如果找不对方法,减肥效果事倍功半还可能会反弹,所以越来越多的人推崇健康科学的减肥理念,把瘦身的重心转移到饮食和运动管理上,于是市场上出现了减肥瘦身自律类的APP软件&#x…

一位27岁软件测试员,测试在职近5年,月薪还不到2W,担心被应届生抢走饭碗

工作了近5年,一个月工资不到20K,担心被应届毕业生取代!互联网的快速发展伴随着员工适者生存的加速,测试员的薪资也在不断增长,以3年、5年、8年为一条分水岭。如果人们的能力和体力不够,他们就会被淘汰。看起…

leetcode 146.LRU 缓存

题目链接:leetcode 146 1.题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中&#xff0…