MATLAB矩阵操作全攻略:从创建索引到性能优化
发布时间:2026/10/2 3:52:50来源:尧图网络
1. 先说清楚MATLAB里为什么矩阵是命根子刚接触MATLAB的人十个里有八个会问同一个问题别的语言都是强调“变量”“函数”“对象”怎么到了MATLAB这里教材第一章永远是矩阵其实道理很简单——MATLAB这名字就是从“Matrix Laboratory”矩阵实验室来的它的整个底层数据模型都是围绕矩阵设计的。你往命令行里敲一个x 5在MATLAB眼里它不是一个“整数变量”而是一个1×1的矩阵你从Excel里读进来一张1000行50列的表它还是一个矩阵。理解了这一点后面所有语法、性能优化、算法实现你都能顺着一条线打通。这篇笔记是我自己入门时第一篇整理的内容定位是给零基础的朋友打地基。我不会讲什么高深的数学推导重点放在三件事上矩阵怎么建、怎么取、怎么算以及每步操作背后的“为什么”。看完你至少能独立完成大部分数值计算和数据处理的前期工作后面学图像处理、信号处理、机器学习这些方向时也不会因为底层数据结构含糊而卡壳。2. 矩阵的创建方式每种方法背后都有对应场景2.1 手工输入适合小矩阵和临时验证最直觉的方法就是直接在命令行里敲方括号行与行之间用分号隔开同一行内用空格或逗号隔开A [1 2 3; 4 5 6; 7 8 9]这段代码会生成一个3行3列的矩阵。我在带新人时经常看到有人搞混“分号”和“逗号”的用途——分号是“换行”逗号或空格是“同一行内分列”。你可以把方括号想象成一个画格子画布从左往右填一行的元素遇到分号就换到下一行继续填。这种输入方式只适合小规模数据。比如你写算法时想手动验证一个2×2矩阵的逆运算结果直接敲进去最省事。但你要是打算用这种方式录入一个100行的大表格那就是给自己找罪受——不仅容易抄错代码也会长得没法看。现在MATLAB是支持中文变量名的比如矩阵 [1 2; 3 4]也能运行。但我个人不建议在正式代码里这么干倒不是技术上有什么问题而是和团队协作时别人的键盘不一定方便打出中文变量而且后续用脚本生成报告时容易出现编码问题。2.2 冒号表达式等差数列生成的黄金工具在矩阵创建里最常用、最需要好好理解的就是冒号表达式x 1:10 % 生成行向量 [1 2 3 4 5 6 7 8 9 10] y 1:2:10 % 生成 [1 3 5 7 9]从1开始步长2 z 10:-2:1 % 生成 [10 8 6 4 2]倒序 t 0:0.1:1 % 生成 [0 0.1 0.2 ... 1.0]冒号表达式的语法是起始值:步长:结束值步长可以省略省略时默认为1。这里有几个新手必踩的坑步长不整除时结束值可能取不到。比如1:0.3:2实际生成的是[1.0 1.3 1.6 1.9]而不是到2结束。MATLAB的规则是“不超过结束值的最大位置”这一点和Python的range有点像。步长正负要和起止顺序匹配。从10数到1步长必须写负的从1数到10步长必须写正的。写反了不会报错但会返回空矩阵这种“静默出错”最坑人。浮点步长有累积误差。你不一定看得出来但底层已经积累了微小误差严谨做采样点时要留意。如果用冒号法做等间距采样我建议用linspace代替x1 linspace(0, 1, 100) % 从0到1均匀取100个点 x2 logspace(-2, 2, 50) % 从0.01到100对数均匀取50个点linspace的优势在于你直接控制点数而不需要关心步长计算。拉长时间轴时logspace在频域分析里几乎天天见。举个实际例子画频率响应曲线时横轴用logspace(-1, 3, 500)一次性生成从0.1Hz到1000Hz的500个点比写0.1:0.01:1000高效得多。2.3 内置函数批量建矩阵不要再用循环去填数新手写代码有个通病需要生成一个全零矩阵时会选择写一个for循环逐格赋值。这个思路在其他语言里没问题在MATLAB里就属于“放着近道不走偏绕远路”。MATLAB内置了大量工厂函数zeros(3) % 3×3全零矩阵 zeros(2, 5) % 2行5列全零矩阵 ones(3, 4) % 全1矩阵 eye(5) % 5×5单位矩阵 eye(3, 4) % 3行4列的对角线为1的矩阵 rand(3) % 3×3均匀分布随机数0到1之间 randn(3) % 3×3标准正态分布随机数 randi(10, 2, 3) % 2行3列元素在1到10之间的随机整数矩阵 diag([1 2 3]) % 以向量为对角线生成对角矩阵关于随机数生成我额外强调一句在做论文或实验时记得在脚本开头设置随机种子。rng(2024) % 固定随机数生成器的种子不设种子的话每次运行rand得到的结果都不一样。很多入门者第一次跑蒙特卡洛模拟时发现两次结果对不上还以为是代码逻辑写错了其实只是没固定种子。zeros和ones还有一个预分配内存的用途这点放到后面讲性能时细说。总之当你需要通过规则生成矩阵时先用这些内置函数再用索引赋值代码会简洁一个量级。3. 矩阵的索引与切片这套规则比其他语言灵活太多3.1 单元素访问与小括号的直觉用法矩阵访问的核心语法是A(行, 列)比如A [10 20 30; 40 50 60]; A(2, 1) % 得到40第2行第1列的元素这里强调一点MATLAB的索引是从1开始的不是从0开始。写过C或者Python的人刚转过来时几乎都会在这里栽跟头——访问A(0, 0)会直接报错“数组索引必须为正整数或逻辑值”。这个错误信息可以说是MATLAB入门阶段出现频率最高的报错之一看到它你就要反应过来索引该从1写起。此外MATLAB支持线性索引也就是只给一个下标时它会按“列优先”的顺序数A [10 20 30; 40 50 60]; A(3) % 得到20这个结果很多人第一反应是懵的。矩阵有两行按列优先数第1列是10、40第2列是20、50第3列是30、60所以第3个元素是20。列优先这个细节在你后面用A(:)把矩阵拉成向量时尤其重要——A(:)得到的结果是把每一列按顺序摞起来的列向量。3.2 切片与end关键字写循环前先想想能不能“切”访问一行、一列或一个子块时用冒号表示“取全部”或“取范围”A [1 2 3; 4 5 6; 7 8 9]; A(1, :) % 第1行的所有列 - [1 2 3] A(:, 2) % 第2列的所有行 - [2; 5; 8] A(1:2, 2:3) % 第1到2行、第2到3列的子矩阵 - [2 3; 5 6] A(end, end) % 最后一行最后一列 - 9 A(end, :) % 最后一行 - [7 8 9]end关键字是MATLAB里我非常喜欢的设计。它表示“当前维度的最后一个索引”好处是你不需要提前知道矩阵尺寸。比如你想把矩阵的最后一行替换成全零行直接写A(end, :) 0而不需要先查size(A, 1)再写数字。按行或按列遍历时很多新手会写for i 1:size(A, 1) row A(i, :); % 处理这一行 end这段代码能用但其实更简洁的方式是直接按行迭代for row A % 注意这里遍历的是列因为MATLAB默认按列方向迭代 end这里有个隐藏细节如果不转置for row A遍历的是每一列而不是每一行。很多从其他语言转过来的人会默认“遍历二维数组第一维是行”结果拿列向量当行向量处理下标全错。所以在循环迭代时要么显式用索引要么时刻提醒自己MATLAB的列优先特性。3.3 逻辑索引最实用也最容易被忽视的高级功能这是矩阵索引里含金量最高的一块。逻辑索引通过一个与矩阵同尺寸的“0/1逻辑矩阵”来选择元素A [1 5 3; 2 8 4; 7 6 9]; B A 4 % B是逻辑矩阵元素大于4的位置为true即1 C A(B) % 取出所有大于4的元素按列优先顺序排列A(B)的结果是一个列向量包含原矩阵中所有满足条件的位置上的值。这种写法在数据清洗时极其舒服——比如你要把矩阵里所有负数替换成0A(A 0) 0;一行代码搞定语义清晰性能也不错。我在做传感器数据处理时经常要做“剔除异常点”的过滤逻辑索引配合isnan、isinf这些判断函数能写出非常优雅的清洗流程。例如data [1.2 NaN 3.4 Inf 5.6]; cleanData data(~isnan(data) ~isinf(data));这里~是逻辑非是逻辑与。isnan判断NaN非数值isinf判断无穷大两个取反后相与就保留下正常数据。这个组合我在实际项目中几乎每周都用好几遍。4. 矩阵的变形操作拼接、重塑、复制4.1 横向拼接与纵向拼接很多时候你需要把几个矩阵组合成一个大矩阵这就要用到拼接操作。语法上可以分为“手动拼接”和“函数拼接”A [1 2; 3 4]; B [5 6; 7 8]; C [A B] % 横向拼接结果是2×4矩阵 D [A; B] % 纵向拼接结果是4×2矩阵方括号里用空格/逗号就是“左右拼”用分号就是“上下拼”这个规则和手动创建矩阵是一致的所以很好记。如果矩阵数量一多或者你想在程序里动态拼接就适合用函数E cat(1, A, B) % 沿着第1维行方向拼接 F cat(2, A, B) % 沿着第2维列方向拼接 G cat(3, A, B) % 沿着第3维拼接得到2×2×2的三维数组cat的好处是维度参数化。在写通用函数时你经常需要写cat(dim, X, Y)这样的形式让调用者自己决定沿哪个方向拼。后面学深度学习时处理四维张量批量数×通道数×高度×宽度的拼接cat几乎是必备工具。4.2 reshape改变形状但不改变数据顺序reshape是另一个高频函数功能是把矩阵重新排列成新的行列结构。它有一个约束变换前后的元素总数必须一致。比如12个元素能变成3×4不能变成5×5。A 1:12; % 1×12的行向量 B reshape(A, 3, 4) % 变成3行4列的矩阵这里要注意排列顺序。reshape默认按列优先读取和填充也就是说如果你有一个1×12的行向量reshape(A, 3, 4)出来的第一列是[1;2;3]第二列是[4;5;6]以此类推。如果你希望按行优先的顺序填充需要先转置B_row_major reshape(A, 4, 3);这个“列优先”的坑会潜伏很久。很多人在做图像处理时把图像从(H, W)拉成向量再reshape回(H, W)结果图变得完全错乱就是因为没有搞清楚读写的优先方向。补充一个实用技巧reshape里面某个维度可以写[]表示让MATLAB自动计算该维度大小B reshape(A, 3, []) % 自动计算出列数为4这个写法在写通用函数时很管用。比如你只知道元素总数想按固定行数重新排列列数自动确定就用reshape(x, n, [])省得自己去算numel(x)/n。4.3 repmat与重复元素矩阵repmat的作用是把矩阵按指定次数复制拼接A [1 2; 3 4]; B repmat(A, 2, 3) % 将A在行方向复制2次、列方向复制3次得到4×6矩阵刚学repmat时我不太理解它的使用场景后来发现它在处理网格数据时非常顺手。比如你要对每个网格点计算距离就需要把一个坐标向量复制成网格形状这时候repmat比循环高效得多。不过新版MATLAB里更推荐用隐式扩展implicit expansion这个下面会专门说。4.4 转置操作.与的区别转置在MATLAB里的符号有点小讲究A是共轭转置A.是非共轭转置。A [12i, 3-1i]; A % 得到共轭转置先转置再对每个元素取共轭 A. % 只是转置复数元素保持原样如果矩阵是实数矩阵这两个符号的结果完全一样。但只要涉及复数用错符号就会导致结果差个负号而且是静默出错。我当年在做一个频域算法时信号通过矩阵运算结果始终不对排查了很久才发现是转置写成了A复数相位全被翻转了。从此养成了习惯只要涉及复数就用A.除非确实想共轭。5. 矩阵运算别把“点运算”看成边角料5.1 常规加减成熟算行列维度必须匹配矩阵加减法是逐元素进行的前提是两个矩阵的尺寸完全一致或者其中一个为标量1×1矩阵A [1 2; 3 4]; B [5 6; 7 8]; C A B % [6 8; 10 12] D A * 2 % [2 4; 6 8]标量与矩阵运算时MATLAB会把标量自动扩展到每个元素上。这就是“标量扩展”也是后面“隐式扩展”的简化版。5.2 矩阵乘法线性代数定义不是逐元素相乘新手最容易混淆的就是A * B和A .* B。A * B是线性代数意义上的矩阵乘法也就是“前一个矩阵的行”和“后一个矩阵的列”做点积。它要求A的列数等于B的行数。比如A [1 2 3; 4 5 6]; % 2×3 B [1 0; 0 1; 1 1]; % 3×2 C A * B % 结果2×2这个“行列匹配”规则在算线性方程组、坐标变换、神经网络前向传播时处处用到。而在许多数据处理场景中你其实想要的是“对应位置元素相乘”这时候要用.*A [1 2; 3 4]; B [5 6; 7 8]; C A .* B % [5 12; 21 32]单凭肉眼.*和*只有一个小点的区别但结果完全不同。我见过太多初学者把深度学习代码里的矩阵乘法写成点乘Loss曲线永远不降最后一步一步追才发现是这里错了。类似地除法也有两套A / B是“右除”解方程X*B AA \ B是“左除”解方程A*X B而A ./ B是逐元素相除。左除\是个高效线性求解符号后面单独说。5.3 隐式扩展循环的“免费午餐”MATLAB从R2016b开始支持隐式扩展。简单说当一个维度大小为1的数组和一个维度更大的数组做运算时MATLAB会自动把大小为1的维度“拉伸”到匹配维度x (1:5); % 5×1的列向量 y 10:10:50; % 1×5的行向量 Z x y % 自动扩展成5×5矩阵早几年写这段代码你得用repmat手动把x和y都扩展成5×5再相加代码繁琐还占内存。隐式扩展替你把这件事做了运行效率和手写扩展基本相当。这个特性在网格生成、距离计算、归一化处理中到处都有用。我举一个实际例子你有一个1000×10的特征矩阵X想对每一列做标准化也就是减去该列均值再除以该列标准差mu mean(X); % 1×10的行向量每一列的均值 sigma std(X); % 1×10的行向量每一列的标准差 X_std (X - mu) ./ sigma; % 隐式扩展每行都减去对应的mu注意mean(X)默认返回行向量所以它和矩阵X做减法时自动沿着行方向扩展每一列都减对应列的均值。这里就体现出隐式扩展的实用价值——不用写循环也不用repmat一行代码完成全矩阵标准化。5.4 用\解线性方程组效率远高于显式求逆矩阵知识绕不开线性方程组。比如2x 3y 8 x 4y 9写成矩阵形式A * [x; y] [8; 9]。MATLAB里最标准、推荐的解法是左除A [2 3; 1 4]; b [8; 9]; sol A \ b这里的\符号表示求解A * X b。它内部会根据矩阵性质自动选择合适算法比如对三角矩阵用回代对对称正定矩阵用Cholesky分解一般方阵用LU分解不需要你手动指定。很多初学者会习惯性地写sol inv(A) * b;这个写法在数学上没错但从数值计算角度看inv(A) * b比A \ b多算了一个不必要的东西完整的逆矩阵既浪费计算时间又可能引入更大的舍入误差。MATLAB官方文档和大量数值线性代数教材都明确建议能用\就不用inv。这不只是“风格问题”在矩阵规模大、条件数不好时inv的数值结果可能差到离谱。5.5 特征值与奇异值从入门开始就要有感知矩阵的基础“算”到特征值分解和奇异值分解算是第一阶段天花板。A [2 1; 1 2]; [V, D] eig(A); % V是特征向量矩阵D是特征值对角矩阵特征值分解要求方阵而且特征向量不一定正交。对于非方阵或者数值稳定性不佳的情况用奇异值分解SVD更靠谱[U, S, V] svd(A);SVD在后面图像压缩、PCA降维、推荐系统算法里都是核心工具。现在很多教程把SVD讲得像天书但其实入门阶段你只需要记住任何矩阵都能做SVD分解出来的奇异值反映了矩阵在不同方向的能量分布。等你后面做图像去噪时直接把小于某个阈值的奇异值清零再重构就是最简单的低秩逼近。6. 矩阵常用操作速查size、numel、disp这些天天见6.1 查尺寸和元素总数A rand(4, 5); size(A) % 返回行向量 [4 5] size(A, 1) % 返回行数4 size(A, 2) % 返回列数5 numel(A) % 元素总数20 length(A) % 返回max(4,5)5注意不是元素总数 ndims(A) % 维度数2length是个容易产生误解的函数。它返回的是“最大维度的大小”不保证等于元素总数。当你对矩阵调用length(A)时如果行数大于列数返回行数反之返回列数。在写通用代码时我几乎总是用size(A, 1)和size(A, 2)而不是length(A)就是为了避免这种不确定性。6.2disp、fprintf与命令行输出调试时最基础的就是打印矩阵内容。直接敲变量名再回车MATLAB会显示变量名和值用disp(A)则只显示值不显示变量名。disp(A); fprintf(矩阵第1行第2列的值是%.2f\n, A(1, 2));fprintf的格式控制符和C语言一致%d表示整数%f表示浮点数%.2f表示保留两位小数\n是换行。在脚本里输出进度信息时fprintf远比disp灵活因为你可以在一个字符串里嵌入多个变量值。6.3 排序、去重、求最值这几个操作在数据处理中几乎天天用A [3 1 4 1 5 9 2 6]; sort(A) % 默认升序 sort(A, descend) % 降序 unique(A) % 去重并排序 max(A) % 最大值 min(A) % 最小值 sum(A) % 所有元素求和 mean(A) % 平均值如果对矩阵做max(A)默认沿着**第1维行方向**操作也就是对每一列分别求最大值返回一个行向量。想对所有元素求一个最大值要写成max(A, [], all)。这里有个常见的简化写法M max(A(:)); % 先拉成向量再求最大值我自己更习惯用max(A, [], all)因为语义更明确。但A(:)这种技巧在老代码里很常见你也需要读懂它。7. 实操中躲不开的“坑”我替你踩过的那些雷7.1 矩阵维度不匹配的报错先看“隐式扩展”有没有生效常见的错误提示是“使用 时矩阵维度必须一致”。这一般是因为两个矩阵的行数或列数对不上。排查思路是先对参与运算的矩阵分别执行size(A)和size(B)打印出来对比。如果其中一个矩阵是行向量另一个是列向量检查是不是想利用隐式扩展——如果是确认你的MATLAB版本是否在R2016b以上。如果是从某个函数返回的矩阵注意函数内部是否对输入做了转置返回值的方向和你预期可能完全不同。7.2clear all和clc的滥用脚本里慎用很多人写的第一个MATLAB脚本就是clear all; close all; clc;我理解大家是想清空环境但这个写法在正式项目里并不可取。clear all会清空所有变量、断点甚至会影响某些类定义如果脚本里还调用了其他自定义函数可能引发不必要的重新初始化。更稳妥的做法是只清理你需要的变量或者用clear variables。clc只是清空命令行显示不影响变量所以它最安全随便用。调试时我一般只在脚本开头保留clc偶尔用clear all但写完正式版后会删掉避免别人运行你的脚本时把Ta自己的环境也清了。7.3 预分配内存为什么循环填充数组这么慢写循环给矩阵逐格赋值时如果数组大小事先未知很多人直接这样写x []; for i 1:10000 x(i) i^2; end这段代码在数据量小的时候感觉不到问题但到几万、几十万次循环时速度会明显偏慢。原因是每次给x(i)赋一个新值如果x的当前容量不够MATLAB需要重新分配一块更大的内存再把旧数据复制过去。这个“扩容-复制”反复发生时间开销非常大。解决办法是先预估大小并预分配x zeros(1, 10000); for i 1:10000 x(i) i^2; endzeros会把数组的内存一次性分配好循环内只做赋值操作不再触发反复扩容。对于更复杂的情况甚至可以直接用向量化运算彻底避免循环i 1:10000; x i.^2;这不是炫技而是MATLAB性能优化的核心思路。养成“能向量化就向量化”的习惯之后你的代码不仅更快可读性通常也更好。7.4 浮点数比较的坑这个问题在矩阵操作里非常隐蔽。你算出来的1.0和另一个路径算出来的1.0用比较时可能是falsea 0.1 0.2; % 实际存储可能是0.30000000000000004 b 0.3; a b % 大概率是false在矩阵代码里这种问题经常出现在“判断矩阵是否对称”“比较两次计算结果是否一致”等场景。正确做法是用abs(a - b) tol其中tol是你根据实际问题设定的容许误差比如1e-6或者1e-8。MATLAB还有isequal函数但对浮点数依然要求严格相等。需要近似比较时可以配合round也可以自定义一个isapprox辅助函数function flag isapprox(x, y, tol) if nargin 3 tol 1e-6; end flag all(abs(x(:) - y(:)) tol); end这个函数对矩阵整体做近似比较在单元测试和结果校验时非常实用。8. 再补三个实用技巧走完一遍笔记后立刻能用上8.1:和end配合矩阵变形当你想把矩阵M的第2列到倒数第2列取出来直接M rand(5, 6); subset M(:, 2:end-1); % 得到5×4矩阵这里不需要知道M原本有多少列2:end-1会自动解析成“从第2列到倒数第1列”。类似的写法在动态数据处理中很常用——比如日志数据的时间戳第一列和最后一列不是特征你要提取中间所有特征列。8.2 使用table还是纯矩阵有些数据带列名、行名比如从CSV读入的数据。基础学习阶段我建议还是先用纯矩阵把特征数据放在一个double矩阵里标签放在另一个向量里。等你熟悉了索引和运算后再接触table类型体验会顺畅得多。反过来一入门就直接挣扎在table的各种取列语法里容易把核心概念搞混。8.3 时刻用size和whos观察数据形状写代码时我几乎每写几步就会停下来看一眼变量的尺寸whos A B C这个命令会列出变量名、大小、字节数、类型。它比单独敲size更直观能一眼看到内存占用。在调试矩阵维度问题时whos是最快的利器。如果你发现自己经常“想当然”地认为某个变量是行向量结果它是列向量那就多用whos能帮你减少一大半报错。9. 最后分享一点我自己的操作习惯矩阵这块内容学完之后我建议你找一个小型数据集哪怕是手写的30行销售记录亲手完成一整条流程读入数据、查看尺寸、用索引挑出满足条件的行、做归一化、算统计量、排序、画一个简单的plot。这套动作几乎覆盖了矩阵入门的所有核心知识点。走完一遍之后你再看其他MATLAB代码不会再有“每个字都认识但连起来看不懂”的错位感。我现在写脚本时还有一个习惯每一段操作之前都先想一步“这一步到底要得到什么形状的矩阵”。想清楚了再动手比反复试错省太多时间。矩阵这个东西形状对了后面的路就顺了形状不对再漂亮的算法代码也跑不出对的数。希望这篇笔记能帮你把地基打扎实下一篇再继续往数据处理和函数设计的方向走。
网站建设高端定制企业官网