
我开始接触MATLAB那会儿最先被震撼到的就是它对矩阵的处理能力。当时用别的语言写个二维数组的操作要循环半天换到MATLAB里几行命令就完事了。这次的学习笔记继续往前推进专门把矩阵这块内容梳理一遍因为矩阵在MATLAB里不是一种普通的数据结构而是整个语言的核心几乎所有功能最终都在跟矩阵打交道。这篇文章围绕矩阵的几个核心方向展开怎么创建和生成矩阵、怎么访问和修改矩阵里的元素、怎么用运算符做矩阵级别的计算、怎么处理分块矩阵和稀疏矩阵这些特殊形态以及怎么在图像处理和数值计算这些实际场景里用好矩阵。适合刚学完MATLAB基础语法、准备进入数组操作的读者也适合已经用过一段时间但对一些底层逻辑还有疑问的朋友。1. 矩阵的创建与生成从手工输入到程序化生成1.1 直接输入法最简单的方式也有讲究MATLAB里创建一个矩阵最直观的方式就是直接敲方括号。比如A [1 2 3; 4 5 6; 7 8 9];这里有个容易混淆的细节在同一行里元素之间用空格或逗号分隔行与行之间用分号分隔。我最早入门时总是把逗号和分号记混后来总结了个土办法——分号在中文里就是“断开一句话”的意思对应矩阵换行这样就好记了。直接输入法适合小矩阵如果是几十行甚至上百行的矩阵手敲就容易出错。我见过有人用Excel生成数据再复制进MATLAB直接在命令窗口粘贴也是可行的但前提是格式必须是方括号加数字的合法输入格式。更稳妥的做法是用readmatrix或load函数从文件读入这个后面会提到。直接输入法还有一个容易被忽略的点数据类型的默认转换。比如B [1 2 3; 4 5 6];这里B默认是double类型哪怕你输入的都是整数它也是double。如果想要整数类型就得显式转换。这点在做图像处理时特别常见图像像素通常用uint8保存但你读进来之后如果想做数值运算往往要先转成double不然计算精度和范围都会受限。1.2 用冒号表达式和linspace/logspace生成规则矩阵冒号表达式是MATLAB里非常高频的一个工具尤其适合生成等差数列。格式是起始值:步长:结束值注意三个元素分别控制起点、间隔和终点x 1:2:10; % 得到 [1 3 5 7 9] y 1:5; % 步长默认为1 z 10:-2:2; % 得到 [10 8 6 4 2]步长可以为负冒号表达式有个边界细节如果结束值不在等差数列上MATLAB会在不超过结束值的范围内截断。比如1:2:10得到的是1、3、5、7、9不会出现11。这个规则看起来简单但在循环索引和时间序列构造时经常引发边界问题得注意。linspace和logspace是另外两个常用的生成函数。linspace(0, 1, 100)生成从0到1均匀分布的100个点而logspace按对数刻度生成适合做频响分析里的频率轴f logspace(0, 3, 50); % 从1到1000对数等间距50个点这里为什么要用logspace而不是直接冒号因为频率分析中我们关心的是倍频关系而不是绝对间隔线性等差数列会把低频部分压缩在一起对数刻度才能把每个频段都显示清楚。这种“什么场景用什么生成方式”的思维比单纯记函数名重要得多。1.3 用内置函数生成特殊矩阵zeros、ones、eye、rand很多刚入门的人觉得zeros、ones这些函数太简单没什么可学的但实际上它们是预分配和构造初始矩阵的主力工具。zeros(m, n)全零矩阵最常用于预分配存储空间。ones(m, n)全1矩阵常用于初始化权重或构造掩码。eye(n)n阶单位矩阵线性代数里的老朋友。rand(m, n)均匀分布随机数矩阵取值在(0,1)区间。randn(m, n)标准正态分布随机数矩阵均值为0方差为1。randi([a, b], m, n)整数随机矩阵范围在[a, b]之间。我在做数值仿真时经常需要批量初始化多个矩阵比如W randn(256, 256) * 0.01; % 小随机扰动初始化 M true(64, 64); % 逻辑矩阵全部为true逻辑矩阵是个容易被忽视的细节。true和false生成的逻辑矩阵在某些索引场景下要比double矩阵高效得多而且用逻辑矩阵做索引是MATLAB里一个很重要的特性后面讲到索引时细说。2. 矩阵的基本操作与访问索引是MATLAB的核心思维2.1 下标索引与线性索引两种思维方式都要掌握MATLAB的矩阵索引和Python差异很大Python的索引从0开始MATLAB从1开始这个切换初期很不适应经常越界。比如A [10 20 30; 40 50 60; 70 80 90]; A(2, 3) % 60第2行第3列 A(8) % 70线性索引按列优先数过去线性索引是按列优先顺序编号的。A(8)对应的元素是第2列的第3个元素也就是70。列优先这个概念对很多习惯行优先语言的人来说是反直觉的但在MATLAB里它却是底层存储的真实方式。理解了这个很多矩阵操作背后的逻辑就通了。除了单个索引也可以切片访问子矩阵A(1:2, 2:3) % 取第1到2行、第2到3列的子矩阵 A(:, 2) % 取第2列整列冒号表示全部 A(1, :) % 取第1行整行这里有个很常见的误区A(1:2, 2:3)返回的是一个2x2的子矩阵但A(:, 2)返回的却是一个列向量不是行向量。有些人会惯性地以为单独取一列也会保持行向量方向结果做矩阵乘法时维度对不上报错。想要保持行向量就得写A(2, :)或者对A(:, 2)做转置。这个方向问题踩过一遍就记住了。还有个end关键字用起来很方便A(1:end-1, 2:end)这里的end在运行时会被替换成对应维度的最大索引比写size(A, 1)-1直观多了。但要注意end在函数里作为索引时是可以用的在普通表达式里不行。2.2 逻辑索引比位置索引更强大的筛选方式逻辑索引是我觉得MATLAB里最值得花时间掌握的技能。它用一个逻辑矩阵每个元素是true或false来选择目标矩阵中对应位置为true的元素A [1 5 3; 8 2 9; 4 7 6]; mask A 5; A(mask) % 返回所有大于5的元素组成的列向量这个操作其实就是在做条件筛选。如果要统计矩阵中满足某个条件的元素数量sum(A 5, all) % 统计所有大于5的元素个数这里sum的all参数是个小知识点它把整个矩阵展平后求和等价于sum(A(:) 5)。如果你写的是sum(A 5)得到的就是每列的和组成的行向量和你想的可能不一样。find函数是和逻辑索引配套的常用工具。它返回满足条件的元素的下标[row, col] find(A 5); [idx_r, idx_c, val] find(A 5);很多时候我们需要的是满足条件元素的位置而不是数值本身。比如在图像处理中找白色像素的坐标或者在信号处理中定位过阈值的时间点用的都是find。2.3 矩阵拼接、变形与转置矩阵拼接有方括号快捷方式和函数方式两种。方括号里用逗号或空格拼接横行方向分号拼接纵行方向A [1 2; 3 4]; B [5 6; 7 8]; C1 [A B]; % 横向拼接得到2x4 C2 [A; B]; % 纵向拼接得到4x2函数式拼接更灵活也更严谨C3 horzcat(A, B); C4 vertcat(A, B); C5 cat(3, A, B); % 第三维拼接常用于堆叠图像通道维度匹配是拼接时的硬规则。横向拼接要求行数相同纵向拼接要求列数相同做错了会直接报错。这个报错信息有时候不直观初学者容易看不太懂其实就是维度对不上的问题。变形和转置是两个容易混淆的操作。转置用单引号它不只是转置还带共轭转置的意思。如果矩阵是复数A得到的是共轭转置想做单纯的转置得用A.。这个区别在处理复数信号时特别重要很多人在FFT结果处理时不小心用错导致频谱数据出现偏差找半天原因。reshape可以改变矩阵的形状但有个前提——元素总数不能变。它按列优先顺序重新排列元素v 1:9; M reshape(v, 3, 3);这里v变成3x3矩阵第一列是1、2、3不是1、4、7。这个排列顺序如果你是行优先习惯非常容易搞反。做图像类的reshape时尤其要当心图像的像素排布和reshape的列优先排列如果不一致出来的图就花了。3. 矩阵的基本运算从元素级到线性代数3.1 点运算和矩阵运算的区别代码正确性的分水岭MATLAB里乘法、除法、幂运算都有两种形式带点的元素级运算和不带点的矩阵运算。A * B矩阵乘法要求A的列数等于B的行数。A .* B元素级乘法要求A和B维度一致对应位置相乘。A / B等价于A * inv(B)这是解线性方程的一种形式。A ./ B元素级除法逐位相除。A ^ 2矩阵自乘多次。A .^ 2每个元素分别平方。点运算就是元素对元素的运算。什么时候用点运算当你对矩阵里的每个元素做同样的操作时就得用点运算。比如把一个灰度图像的所有像素值除以255归一化就是img / 255这里标量和矩阵相除不需要点但如果是两个图像矩阵对应位置做除法就得用./。最常翻车的场景是多项式计算。比如计算y x^2 3*x 5如果x是向量直接写x^2就会报错因为矩阵乘法里向量乘向量要么维度不对要么结果是内积而不是平方。正确写法是x.^2 3*x 5。这种问题几乎每个MATLAB初学者都会遇到包括我自己在内当年在这个坑上浪费了不少时间。报错信息会提示“Matrix must be square”之类初学者容易懵。看到这种错误第一反应就应该是“我是不是忘了加点”来看个典型的对比x [1 2 3 4]; y1 x .^ 2; % [1 4 9 16] y2 x * x; % 30内积 y3 x * x; % 4x4外积矩阵同样一组数用不用点运算、用哪种排列得到的数学意义完全不一样。在MATLAB里写代码脑子里要时刻清楚我要做的是“矩阵层面”的操作还是“元素层面”的操作这两个世界的规则截然不同。3.2 转置、逆矩阵、行列式与秩线性代数核心操作MATLAB在线性代数方面非常方便直接调用函数就行det(A)行列式。inv(A)矩阵求逆。rank(A)矩阵的秩。trace(A)矩阵的迹对角线元素之和。cond(A)条件数衡量矩阵的病态程度。求逆运算在数值计算中不建议滥用。很多教材里的公式会写x A^{-1} b看起来简洁但实际数值计算时用A \ b左除或b / A右除比显式求逆更稳定。原因在于求逆本身要解线性方程组而A \ b用的是高斯消元或LU分解这类数值方法避免了构建逆矩阵带来的额外误差。我刚学MATLAB时就犯过这个错误为了算一个最小二乘解直接写了x inv(A * A) * A * b然后发现结果和课程答案差了一点。后来用x A \ b一跑误差立刻小了。原因就是A * A这一步把条件数平方放大了数值误差被放大显式求逆又加重了这个问题。在MATLAB里解线性方程组的最佳实践就是A \ b这是所有线性代数相关问题最优先的选择。行列式的用途在于判断矩阵是否奇异。如果det趋近于0说明矩阵接近奇异求逆会得到极其敏感的结果。但需要注意det非常小并不一定代表矩阵不可逆还要结合条件数和尺度来看。工程上更可靠的做法是检查cond(A)的值条件数过万就要小心了。rank函数在MATLAB里用的是SVD奇异值分解来判断有效秩不是直接数主元。这是在数值上最稳健的做法但也意味着如果你的矩阵含噪声rank可能变成满秩。要控制秩可以自己设定容差或者在SVD之后自己看奇异值分布。3.3 特征值分解与奇异值分解特征值分解是最常见的矩阵分解之一。MATLAB里用eig函数[V, D] eig(A);V的每一列是特征向量D是对角矩阵对角元素是特征值。要验证结果是否正确norm(A * V - V * D, fro) % 应该约等于0特征值分解要求矩阵是方阵而且如果矩阵不对称或有重特征值特征向量矩阵可能条件不佳。工程上更通用的是SVD奇异值分解用svd函数[U, S, V] svd(A);SVD的优点是对任意矩阵都能做而且在数值稳定性上优于特征值分解。在图像压缩里常用SVD做低秩近似保留最大的几个奇异值丢弃小奇异值就能在不大损失图像质量的情况下大幅压缩数据。这个思路在推荐系统里也能见到本质上就是矩阵降维。做特征值分析时还有个实际用途值得提——判断线性系统的稳定性。控制系统理论里状态矩阵的特征值如果都落在左半平面系统就是稳定的。我在做动态系统仿真时经常用eig(A)来判断模型是否发散这比跑一遍仿真观察结果要快得多也直观得多。4. 特殊矩阵形态与实用技巧分块、稀疏、逻辑矩阵、图像矩阵4.1 分块矩阵的构造与操作分块矩阵在MATLAB里非常自然因为拼接本身就是构造分块矩阵的手段。比如A [1 2; 3 4]; B [5 6; 7 8]; C [9 10; 11 12]; D [13 14; 15 16]; M [A B; C D];这就得到了一个4x4的分块矩阵。分块矩阵的关键优势在于有些情况下我们不需要展开整个大矩阵而是利用块的代数性质直接计算逆或乘积这在优化算法和大规模科学计算中经常用到。比如2x2分块矩阵的求逆公式Schur补% M [A B; C D] % inv(M) [inv(A) inv(A)*B*Schur_inv*C*inv(A), -inv(A)*B*Schur_inv; % -Schur_inv*C*inv(A), Schur_inv] % 其中 Schur D - C*inv(A)*B这个公式不是让你手工算但理解它有助于设计稀疏求解器和高效算法。如果你只是需要数值结果直接inv(M)就行。但如果M的规模很大且各块有特殊结构直接求逆可能内存爆炸用Schur补把问题拆成小块逐步处理反而高效得多。我的建议是小矩阵、一次性计算无脑用inv或\大规模矩阵、重复计算、结构特殊矩阵花时间做分块处理非常值得。4.2 稀疏矩阵大规模矩阵的内存救星稀疏矩阵是MATLAB里一个很重要的概念但基础教材往往只提一句。真实工程中很多矩阵是稀疏的——大部分元素为零。比如电路仿真里的节点导纳矩阵、有限元里的刚度矩阵、图论里的邻接矩阵都是典型的稀疏矩阵。MATLAB用sparse函数创建稀疏矩阵或者用spdiags创建对角线型的稀疏矩阵A sparse(10000, 10000); % 1亿个元素稀疏存储却几乎不占空间 A(1, 1) 5; B spdiags(ones(1000, 3), [-1 0 1], 1000, 1000); % 三对角矩阵稀疏矩阵存储只记录非零元素的位置和值所以内存占用远小于普通矩阵。在矩阵规模较大的场景比如图像尺寸1万x1万用普通稠密矩阵分分钟内存爆掉换成稀疏矩阵就很从容了。操作上稀疏矩阵和普通矩阵有很多函数可以通用但有一个点要注意很多逻辑判断的函数对稀疏矩阵支持不好。比如普通矩阵可以用全零判断技巧稀疏矩阵就要用nnz函数统计非零个数。在做完线性方程组求解后如果想转回普通矩阵做进一步逐元素操作用full函数。4.3 图像的本质就是矩阵图像处理是MATLAB矩阵能力最直观的应用场景之一。一张灰度图像本身就是一个二维矩阵每个元素的值代表像素亮度。RGB彩色图像则是一个三维数组第三维分别是红绿蓝三个通道。img imread(photo.jpg); whos img % 查看数据类型和维度 gray rgb2gray(img); % 转灰度得到二维矩阵对图像做平移、旋转、缩放本质上是矩阵变换或插值。对图像做滤波本质上是矩阵卷积。MATLAB里图像处理的很多操作底子都是矩阵运算。这里要特别提醒一个坑imread读进来的是uint8类型取值范围0到255。如果你直接用double矩阵的思维去操作它比如做归一化时直接除以255结果在uint8里就被取整了图像数据会出问题。正确做法是先转成double再操作img_d double(img); img_norm img_d / 255;同样保存图像时记得转回uint8。在MATLAB中处理图像时类型转换是小细节却总容易出问题花了很多时间排查后才发现是类型不匹配。4.4 混淆矩阵机器学习评估中的矩阵实例在机器学习和分类任务中混淆矩阵Confusion Matrix是评估模型性能的工具本质也是一个矩阵行代表真实类别列代表预测类别。比如二分类问题% 假设真实标签为y_true预测标签为y_pred confMat confusionmat(y_true, y_pred);输出的confMat是一个2x2矩阵主对角线是预测正确的样本数副对角线是分类错误的样本数。我最早看到这个矩阵时没什么感觉后来自己手动用循环累加构造了一个发现浪费了很多时间。用MATLAB自带的confusionmat一行搞定从这些类工具函数的情况能看出MATLAB在数据处理方面的积累。从混淆矩阵还可以派生出一系列评估指标比如精确率Precision、召回率Recall、F1分数等。这些指标的计算本质上就是矩阵元素的比例运算。理解混淆矩阵对理解分类模型的表现很有帮助——特别是当类别不均衡时光看准确率会失真混淆矩阵能暴露这种问题。5. 矩阵运算进阶与性能优化从“能跑”到“跑得快”5.1 向量化摆脱循环思维初学MATLAB的人最容易犯的一个错误就是把别的语言的循环习惯带进来% 不好的做法用循环逐个操作 for i 1:n y(i) x(i) * 2 1; end % 好的做法直接向量化 y x * 2 1;MATLAB的循环效率在解释型语言里已经优化得不错了但和向量化运算比还是差很远。原因是MATLAB的核心计算矩阵乘法和元素级运算都能调用优化过的底层库如果能把问题抽象成矩阵运算性能提升会非常明显。向量化也不只是性能问题代码的可读性也会好很多。我见过有人处理一个1000x1000的矩阵对每个元素做条件判断用双重循环写了十几行换成逻辑索引加赋值操作三行就搞定了A(A 0) 0; % 把所有负数置零这行代码读起来非常自然几乎就是英语语义的直接翻译。这就是MATLAB的独特之处——写矩阵运算有时候就像在描述数学本身。5.2 预分配内存性能翻倍的常见优化还有一个高频性能问题是动态数组增长。很多人在循环里写x []; for i 1:10000 x(i) i^2; end这是效率极低的做法。因为每次扩展数组MATLAB都要重新分配内存、拷贝旧数据像滚雪球一样越滚越慢。正确做法是用zeros预分配空间x zeros(1, 10000); for i 1:10000 x(i) i^2; end这个改动看着很小实际速度差异在矩阵规模大时能有几个数量级。所以不要嫌预分配麻烦这基本是写高效MATLAB代码最基本的一条要求。5.3 大规模矩阵的常见陷阱内存、类型和维度处理大规模矩阵时有几类问题经常遇到。第一个是内存爆炸。矩阵是先分配内存再赋值的如果一次性声明一个超大矩阵内存不够直接报错甚至让系统卡死。解决办法是改用稀疏矩阵或者分块加载数据。第二个是类型不匹配。比如两个矩阵都是double但一个是单精度一个是双精度运算时低精度会自动提升到高精度这个过程可能隐藏一些精度损失的问题。如果对精度有要求运算前最好统一用single、double或int显式转换。第三个是维度不匹配。矩阵相乘要求第一个矩阵的列数等于第二个矩阵的行数矩阵加法则要求维度完全一致。当这个不一致时报错信息会指出“Matrix dimensions must agree”。要注意的是一些特殊情况比如矩阵加一个标量是合法的因为标量会自动扩展广播但向量加一个行向量维度对不上就会报错。5.4 常见问题与排查经验平时用MATLAB做矩阵操作踩坑最多的是下面几个列出来供参考现象可能原因解决办法Matrix must be square对非方阵用了矩阵幂运算或特征值分解检查是否忘了点运算改用.^Matrix dimensions must agree对应维度不一致检查转置、拼接方向必要时用size查维度结果全是NaN或Inf除零、未初始化、矩阵奇异用isnan、isinf定位检查是否除以零图像显示一团黑或花屏数据类型或数值范围不对转double并归一化到0-1之间或者转uint8内存不足动态增长、稠密大矩阵预分配、用稀疏矩阵、分批处理运算结果和理论不一致忘了共轭转置、用了复数转置用.做普通转置做共轭转置这些坑我多多少少都踩过。尤其是第一类几乎每个MATLAB初学者都会碰到。后来养成一个习惯只要涉及乘方、乘法、除法先停下来想一下“这里到底是要元素级操作还是矩阵操作”代码出错率就大幅下降了。6. 矩阵在典型场景中的组合应用6.1 线性方程组求解左除右除的选择求解Ax b这个经典问题MATLAB提供了多种方式x A \ b; % 推荐相当于 inv(A)*b 但数值更稳定 x inv(A) * b; % 不推荐可能数值误差更大 x pinv(A) * b; % 当A不可逆时超定或欠定使用伪逆A \ b是左除b / A是右除两者的区别在于未知数的排列。如果你是做最小二乘拟合超定方程方程数多于未知数时用A \ b会自动得到最小二乘解不需要手动构造正规方程。这个特性在做数据拟合时非常实用。6.2 图像处理中的矩阵变换与阈值分割图像本身是矩阵图像处理就变成了矩阵变换问题。比如图像旋转img_rot imrotate(img, 45, bilinear, crop);这里的插值方法bilinear和裁剪选项crop是可以用矩阵运算重新实现的细节。更本质的仿射变换则对应着变换矩阵tform [1 0 0; 0 1 0; tx ty 1]; img_translated imwarp(img, affine2d(tform));在做阈值分割时通常用逻辑索引实现binary_mask gray_img 128;这个操作把所有像素大于128的位置变成true其余为false然后就可以基于这个逻辑矩阵做掩码处理、连通域分析等操作。整个过程就是矩阵和逻辑矩阵之间的转换简洁到几乎不需要额外解释。6.3 数值计算中的矩阵分解与稳定性分析数值计算里矩阵分解是很多算法的核心。LU分解解线性方程组、Cholesky分解解对称正定系统、QR分解解最小二乘问题这些在MATLAB里都有对应函数lu(A)、chol(A)、qr(A)。我在工作中用QR分解处理过一段时间的曲线拟合问题。原因是正规方程法在数据量小时快速但数值稳定性不足而QR分解能在不放大条件数的情况下给出稳定的最小二乘解。如果对数值稳定性有要求用A \ b背后就是QR或相关分解所以结果往往更可靠。条件数的计算和病态矩阵的识别也是一个重要应用。什么是病态矩阵可以用一个生活类比输入的微小变化导致输出巨大变化就像按方向盘时轻轻一转车却像失控一样猛拐。数值上条件数就是度量这种“被放大的灵敏度”的指标。如果cond(A)大于10的12次方量级基本可以认为矩阵是数值上奇异的直接求逆得到的结果毫无意义。这时需要做正则化或SVD截断。7. 命令窗口与脚本开发的常用技巧7.1 分号的作用控制输出、拼接与隐藏运行过程在MATLAB里分号有三种用途初学者容易混淆。第一种是作为矩阵行的分隔符比如A [1 2; 3 4];。第二种是在代码行末尾抑制输出比如x 1:10;运行时不会在命令窗口显示x的内容。第三种是在脚本中分隔多条命令比如a 1; b 2;。很多初学者写脚本时总是忘记加分号运行后命令窗口刷出一大片结果既影响视野又拖慢网速。说实话我自己早期也有这个习惯。后来形成肌肉记忆赋值语句一律加分号需要查看结果时才不加。7.2 变量显示与格式控制查看矩阵内容有几种方式disp(A) % 简化显示 fprintf(A(1,1) %.2f\n, A(1,1)) % 指定格式对于大矩阵直接显示会刷屏可以用head(A)或tail(A)查看前几行或后几行需要较新版本或者手动限制显示范围A(1:5, 1:5)如果觉得显示精度太丑用format命令调整format short % 默认4位小数 format long % 更多位小数 format rat % 分数显示 format compact % 去掉空行在调试时我特别常用format rat来看一个矩阵是否是精确的某种结构有时候比看小数更能暴露问题。7.3 路径、文件和数据的读写矩阵数据的持久化是实际工作中必定会遇到的问题。保存和加载用save和load就够了save(mysession.mat, A, B, C); % 保存指定变量 load(mysession.mat); % 加载所有变量如果是要和外部程序交互CSV文件和文本文件更通用writematrix(A, matrix_output.csv); A2 readmatrix(matrix_output.csv);这里有个数据精度的坑。默认情况下writematrix用较少的有效数字写入文件如果你的矩阵数值很小或很大精度可能在写入时就丢了。建议在写入时指定精度参数或者在高精度要求下用save保留MATLAB原生格式。这个细节在实际工作中很容易被忽略但影响可能很大。8. 一个综合实例用矩阵运算实现简单的图像模糊效果既然矩阵是MATLAB的核心我拿一个具体场景来串一串前面的大部分知识点——给图像做均值模糊。思路是用一个局部窗口对图像每个像素邻域取平均。这里的关键是向量化地实现滑动窗口操作而不是用三重循环慢慢跑。假设灰度图像是Idouble类型尺寸m x n核大小是3x3。可以这样实现[m, n] size(I); I_pad padarray(I, [1 1], replicate); % 边界用复制填充 % 提取9个局部棋盘位置 I1 I_pad(1:m, 1:n); I2 I_pad(1:m, 2:n1); I3 I_pad(1:m, 3:n2); I4 I_pad(2:m1, 1:n); I5 I_pad(2:m1, 2:n1); I6 I_pad(2:m1, 3:n2); I7 I_pad(3:m2, 1:n); I8 I_pad(3:m2, 2:n1); I9 I_pad(3:m2, 3:n2); I_blur (I1 I2 I3 I4 I5 I6 I7 I8 I9) / 9;这个示例把前面讲到的矩阵切片多行多列切片、矩阵拼接的一些思想融合在一起。它比imgaussfilt(I, 2)或者conv2(I, ones(3)/9, same)更“基础”因为它把每一步怎么取邻域、怎么对齐都展示出来了。对比这三种写法可以发现conv2一行代码就能完成同样的功能而且是更通用的做法kernel ones(3, 3) / 9; I_blur conv2(I, kernel, same);这就是矩阵运算的威力——一个卷积操作本质上就是局部窗口乘累加底层全部是矩阵乘法。如果手工一步步循环不仅慢得多代码也长得多。理解了底层逻辑用现成函数时会更有底气。平时我在教学或者写博客时经常会先说“不用工具函数怎么做”再说“用工具函数怎么写”这样能帮助建立底层逻辑直觉又能提高工作效率两条腿走路。9. 学习矩阵常见的困惑与应对思路9.1 学矩阵运算有没有必要先补线性代数我的看法是线性代数知识不需要学得很深但基本概念必须有。至少要知道矩阵乘法、转置、逆、特征值、行列式分别是什么含义。MATLAB的学习反过来也会加深对线性代数的理解——很多抽象的概念在亲自操作之后会变得具体起来。比如行列式这个概念课本上可能只是定义了计算公式但你在MATLAB里试着改变一个矩阵的元素看det如何变化就能直观感受到行列式对矩阵奇异性判断的作用。这种学习方式比单纯看书效率高很多。9.2 如何培养“矩阵思维”“矩阵思维”是伴随MATLAB使用逐渐形成的一种看问题方式——把一组数据看作一个整体用一次操作处理整个数据集而不是一个个循环去遍历。培养这个思维可以从几个小练习开始试着用矩阵运算完成多组数据的归一化试着用逻辑索引筛掉异常值试着用矩阵乘法完成多组线性变换。每当你发现自己想写循环时先停下来想想能不能用矩阵运算替代。这个过程一开始会感觉别扭但用熟练之后效率提升非常显著。9.3 多尝试MATLAB自带函数和工具箱MATLAB的各个工具箱Toolbox是它生态的一大优势。图像处理有Image Processing Toolbox符号计算有Symbolic Math Toolbox统计有Statistics and Machine Learning Toolbox。这些工具箱里的函数基本上都围绕矩阵数组展开学会了矩阵工具箱学习曲线就平坦很多。我有段时间做了比较多阵列信号处理的工作用的就是Phased Array System Toolbox。打开文档发现它每一步操作本质上还是矩阵运算——波束成形是矩阵乘协方差估计是矩阵乘加特征分解是特征值问题。理解了这一点之后用工具箱不再是查函数拼拼凑凑而是可以猜测出它内部在做什么遇到问题排查起来更有方向感。10. 最后再分享一些实用小技巧10.1 千万别用循环累加构造大矩阵以前我处理一个数据量较大的脚本时用循环逐个累加填充了一个5000x5000的矩阵运行速度极慢。后来预分配后速度有所提升但真正起飞是在改用向量化生成之后——整整快了几十倍。MATLAB最擅长的还是整体数组运算循环留给有必要的场景去用。10.2 用size和whos确认维度与内存调试矩阵问题时第一件事就是确认变量维度、类型、占用内存size(A) whos A很多时候报错不必细看先查这两个信息问题往往就一目了然。10.3 善用try避免脚本中断在批量处理多个文件或多种情况时某个矩阵可能出现奇异或维度不符脚本会因此中断。用try和catch包住可能失败的代码段先记录错误继续处理剩下的数据整体效率明显更好try x A \ b; catch ME warning(求解失败%s, ME.message); x NaN(size(b)); end这类容错处理在实际项目中很常见。数值计算不会总是按照理想情况运行对异常情况保持预期并提前写好应对逻辑是工程经验和学术代码分水岭的地方之一。