MATLAB嵌套循环向量化报错:维度不匹配,求全向量化方案
一、维度不匹配错误的原因及修复
首先来说你遇到的报错问题:当你把内层循环改成批量赋值后,出现"Subscripted assignment dimension mismatch",核心原因是赋值左右两边的维度不兼容。
原来的内层循环是逐行赋值:每次j是单个索引,mat(j,:)是1行i列的行向量,这时候不管getVal(i)返回的是行向量(1×i)还是列向量(i×1),MATLAB都能自动适配(列向量会被转置成行向量匹配)。但改成j=1:m后,mat(j,:)变成了m行i列的矩阵区域,这时候就要求右边的getVal(i)输出维度能和这个区域匹配:
- 如果
getVal(i)返回的是i×1的列向量,右边是i行1列,左边是m行i列,维度完全对不上,直接报错; - 如果
getVal(i)返回的是1×i的行向量,MATLAB的广播机制其实能自动把行向量重复m次填充到左边,但显然你的getVal(i)返回的是列向量,这才是问题所在。
快速修复内层循环
只需要把getVal(i)的输出转置成行向量就行:
for i = 2:n mat = NaN(m,i); j = 1:m; mat(j,:) = getVal(i).'; % 转置列向量为行向量 num(i) = max(mat(:)); end
或者更简洁的,用repmat直接生成整个矩阵,省去手动赋值的步骤:
for i = 2:n val = getVal(i); mat = repmat(val(:).', m, 1); % 先把val转成行向量,再重复m行 num(i) = max(mat(:)); end
二、彻底向量化整个循环(干掉所有for循环)
其实你原来的代码藏着一个巨大的优化空间:mat里每一行都是getVal(i)的副本,所以整个矩阵的所有元素就是getVal(i)的元素重复了m次,那max(mat(:))其实就等于max(getVal(i))——完全没必要生成那个大矩阵!
基于这个结论,我们可以直接跳过矩阵生成,把整个外层循环也向量化:
方案1:用arrayfun实现
num = NaN(1, n); % 先初始化num数组 num(2:n) = arrayfun(@(k) max(getVal(k)), 2:n);
arrayfun会自动遍历2到n的每个k,调用getVal(k)并取最大值,直接把结果赋值给num(2:n),一行代码搞定外层循环。
方案2:如果能修改getVal(可选)
如果你有权限修改getVal函数,让它支持向量输入(比如输入2:n返回一个单元格数组,每个元素对应单个i的结果),还能更高效:
valCells = arrayfun(@getVal, 2:n, 'UniformOutput', false); num(2:n) = cellfun(@max, valCells);
不过如果getVal只能接受单个标量输入,第一种方案就足够好用了。
额外小贴士
原来的代码里先生成NaN矩阵再覆盖,其实完全是多余的操作——直接生成需要的矩阵或者跳过矩阵生成,能节省大量内存和计算时间,尤其是当m和n比较大的时候。
内容的提问来源于stack exchange,提问作者StaticCrazee

