目录
一个用matlab写代码的人,不会用索引,就如同不会骑自行车的人在推自行车。
matlab的很多函数的返回值都可以提供索引,方便后续操作。
1. 标量index基础
要求
已知矩阵a,设a的第1行的最小值为x,其所在列为第k列,记a的第2行第k列元素为y,求x与y的乘积。
一般写法
将a的第一行第一列的元素赋值给x,并初始化位置标记position为1,从第2列开始搜索,如果有元素小于x,就更新x和position
a = [3 9 4 5 2 7; 6 8 3 3 1 4];
x = a(1,1);
position = 1;
for i = 2: length(a)
if a(1, i) < x
x = a(1, i);
position = i;
end
end
y = a(2,position);
索引写法
这里的a(1,:)表示的是矩阵a的第一行,函数min会将输入向量的最小值传给x,最小值的索引传给index
a = [3 9 4 5 2 7; 6 8 3 3 1 4];
[x, index] = min(a(1,:));
y = a(2, index);
在工作区里观察各个变量可以发现,index的值是5,是一个标量。
事实上,这里的index也可以换成向量。
2. 向量index基础
要求
找出矩阵a的第1行中小于4的元素
一般写法
设置一个空向量find,遍历矩阵a的第1行,如果有元素小于4,就将它放入find向量中
a = [3 9 4 5 2 7; 6 8 3 3 1 4];
find = [];
for i = 1: length(a)
if a(1, i) < 4
find = [find a(1,i)];
end
end
索引写法
a = [3 9 4 5 2 7; 6 8 3 3 1 4];
index = a(1,:) < 4;
find = a(1, index);
这里a(1,:)的值为[3 9 4 5 2 7],是一个一维向量。
a(1,:) < 4会将该向量中的每一个值与4进行比较,返回一个长度与a(1,:)相同的布尔向量,0表示对应位置的值不小于4,1表示对应位置的值小于4。
所以index的值为 [1 0 0 0 1 0]
a(1, [1 0 0 0 1 0])的实际意义:首先取出a矩阵的第一行,接着按照向量[1 0 0 0 1 0]取出对应位置为1的值
3. index进阶与运用
进阶
结合上述两点,可以知道下面的b和c是相等的,结果都是[9 4]
a = [3 9 4 5 2 7]; %一维向量
b = a([2 3]) %标量写法
c = a(logical([0 1 1 0 0 0])) %向量写法
这里说明一下,标量写法不是说一次只能取一个值,在这里标量写法同时取了两个值。
这里logical的意思是将普通向量转换成为布尔向量,所有为0的值变为False,所有不为0的值变为True。只有布尔向量才能进行正常的index索引操作。
因为logical函数,所以下面这3句也是等价的
c = a(logical([0 1 1 0 0 0]))
d = a(logical([0 2 3 0 0 0]))
e = a(logical([0 4 8 0 0 0]))
运用
对于下面代码的理解欢迎在评论区讨论~
%% Preparation
clear
a = [3 9 4 5 2 7;
6 8 3 3 1 4;
7 2 2 6 3 5];
%% Example 1
% Abbreviated
b_Abbreviated = a(:,a(1,:)<4);
% General
index = a(1,:) < 4;
b_General = a(:,index);
%% Example 2
% Abbreviated
c_Abbreviated = a(a(:,4)>3,:);
% General
index = a(:,4) > 3;
c_General = a(index,:);
%% Example 3
d_Abbreviated = a;
d_General = a;
% Abbreviated
d_Abbreviated(a(:,4)>3, a(1,:)<4) = NaN;
% General
index1 = a(:,4) > 3;
index2 = a(1,:) < 4;
d_General(index1,index2) = NaN;
实际的应用场景会比我的例子复杂很多,但是万变不离其宗,掌握索引你就掌握了matlab的灵魂。
5. Why index ?
很多人看了我写这么多,可能感觉index也没什么用,也就是把原本五六行代码变成了两三行代码,又不好理解。
其实不然,当数据量增大的时候,使用索引的方法的运行速度会是普通写法的数十倍。
要求
有一个6000*6000的随机矩阵,矩阵元素的范围在0到1之间。现需将该矩阵中小于0.5的元素赋值为0。
普通写法
clear
a = rand(6000,6000); % 生成0-1的6000*6000的随机浮点数矩阵
for i = 1: 6000 % 遍历行
for j = 1: 6000 % 遍历列
if a(i,j) < 0.5 % 判断当前元素是否小于0.5
a(i,j) = 0; % 对满足条件的元素赋值
end
end
end
index写法
clear
a = rand(6000,6000); % 生成0-1的6000*6000的随机浮点数矩阵
matrix = a < 0.5; % 得到小于0.5的索引矩阵
a(matrix) = 0; % 通过索引矩阵对满足条件的元素执行赋值
在我的电脑上,普通写法需要运行3.8s,而索引写法只需要0.38s。
为什么索引写法的效率会这么高呢?我认为他可能是并行执行的。
普通写法是遍历了a(i,j)才能接着遍历a(i,j+1),相当于一个人在数36000000个元素,每次数,都需要判断这个元素是不是小于0.5,如果小于还需要对这个元素执行赋值操作。
而索引写法是并行遍历,遍历的元素没有先后关系,相当于七八个人(线程)在一起数打乱的36000000个元素,把小于0.5的元素的位置记下来。接着这七八个人一起给记录的位置赋值。
为了更直观地说明这个问题,我制作了两幅GIF如下。
可以看到同样是20*20的矩阵,单线程顺序遍历的速度远不如多线程随机遍历的速度。