![d2eca0313c61a51bb62b7bafdbfc5221.png](https://i-blog.csdnimg.cn/blog_migrate/cb8504b8699ac351bd792c3bd354c820.jpeg)
知道 permute 函数的请直接跳到第二节
转置函数介绍
MATLAB 中,转置符号大家应该都非常熟悉,不加点是共轭转置,加点是直接转置。
>> a = rand(2,4) + rand(2,4)*1i
a =
0.9575 + 0.4218i 0.1576 + 0.7922i 0.9572 + 0.6557i 0.8003 + 0.8491i
0.9649 + 0.9157i 0.9706 + 0.9595i 0.4854 + 0.0357i 0.1419 + 0.9340i
>> a'
ans =
0.9575 - 0.4218i 0.9649 - 0.9157i
0.1576 - 0.7922i 0.9706 - 0.9595i
0.9572 - 0.6557i 0.4854 - 0.0357i
0.8003 - 0.8491i 0.1419 - 0.9340i
>> a.'
ans =
0.9575 + 0.4218i 0.9649 + 0.9157i
0.1576 + 0.7922i 0.9706 + 0.9595i
0.9572 + 0.6557i 0.4854 + 0.0357i
0.8003 + 0.8491i 0.1419 + 0.9340i
但是对于高维数组,如果想更改维度,就需要广义转置函数 permute。
首先创建一个四维数组:
>> a = rand(3,4,1,2)
a(:,:,1,1) =
0.4018 0.1233 0.4173 0.9448
0.0760 0.1839 0.0497 0.4909
0.2399 0.2400 0.9027 0.4893
a(:,:,1,2) =
0.3377 0.1112 0.2417 0.1320
0.9001 0.7803 0.4039 0.9421
0.3692 0.3897 0.0965 0.9561
然后利用 permute 函数可以切换它的维度,函数的第一个参数是待切换维度的数组,第二个参数是一个向量,其长度与数组维度相同,向量每个位置的数值代表这个位置应该放置原来数组的哪个维度。比如下面的 4 1 2 3,就代表将原来数组的第 4 维转到第 1 维上,第 1 维转到 第 2 维上,第 2 维转到第 3 维上,第 3 维转到第 4 维上。观察新矩阵每一片的列,以第一个数 0.4018 为例,它的下一个数是 0.3377,正是原数组在第 4 维上移动一个到达的数,这就是原来的第 4 维到了第 1 维上的体现。因为第 3 维转到了第 4 维,而且第 3 维的长度为 1,所以新数组在最高的维度,第 4 维,长度为 1,实际上是一个 3 维数组。所以原数组显示的时候每一片的提示 a(:,:,1,2) 而新数组每一片的提示是 a(:,:,1)。
>> permute(a,[4 1 2 3])
ans(:,:,1) =
0.4018 0.0760 0.2399
0.3377 0.9001 0.3692
ans(:,:,2) =
0.1233 0.1839 0.2400
0.1112 0.7803 0.3897
ans(:,:,3) =
0.4173 0.0497 0.9027
0.2417 0.4039 0.0965
ans(:,:,4) =
0.9448 0.4909 0.4893
0.1320 0.9421 0.9561
利用 size 函数也可以看出 permute 对于维度的操作:
>> size(a)
ans =
3 4 1 2
>> size(permute(a,[4 1 2 3]))
ans =
2 3 4
ipermute 函数分析
首先说明 ipermute 函数的作用
>> permute(a,[4 1 2 3]);
>> ipermute(ans,[4 1 2 3])
ans(:,:,1,1) =
0.4018 0.1233 0.4173 0.9448
0.0760 0.1839 0.0497 0.4909
0.2399 0.2400 0.9027 0.4893
ans(:,:,1,2) =
0.3377 0.1112 0.2417 0.1320
0.9001 0.7803 0.4039 0.9421
0.3692 0.3897 0.0965 0.9561
可以看到,在对 a 进行广义转置后,如果想复原 a,可以直接对结果调用 ipermute 函数,第二个参数与之前输入给 permute 函数的完全一样,然后输出就是原来的 a 了。但是,这个函数是如何实现的?
MATLAB 中一些函数可以使用 edit 命令直接查看它们的源码(如果全是注释的说明是二进制形式的底层函数,如果是 p 函数会提醒无法查看),比如这个 ipermute 函数。
edit ipermute.m
之后可以看到除了注释和定义外,这个函数正文就两行
inverseorder(order) = 1:numel(order); % Inverse permutation order
a = permute(b,inverseorder);
第二行还就是个 permute,这就非常有意思了,怎么一行就把寻找还原需要的顺序这事给干了,我们要看看第一行到底干了啥。
于是我们单独执行这个第一行,其中 order 还是使用刚才用过的 [4 1 2 3]
>> order = [4 1 2 3]
order =
4 1 2 3
>> 1:numel(order)
ans =
1 2 3 4
>> inverseorder(order) = 1:numel(order)
inverseorder =
2 3 4 1
也就是说,首先创建一个自然数序列,然后把这个自然数序列按照之前指定的位置给放过去就行了?这是为什么?
让我们先分析一个。以原数组的第 4 个维度为例,一开始,第 4 个维度被挪到了第 1 个维度上,那么之后,自然是应该把第 1 个维度挪到第 4 个维度上,如果需要再次用 permute 来完成这种转换,那么需要的就是将第二个输入参数的第 4 个设置成 1。
对于 order 来说,一开始,我们是把 order 看成名单,permute 的操作是“根据名单把对应的维度找来”,比如第 1 项,就是
“根据第 1 项的 4,把第 4 个维度找来,放到第 1 项这里,也就是新的第 1 维变成了原来的第 4 维。”
但是在构造 inverseorder 这个向量时,这个 order 就变成了路标,具体的操作可以理解为“根据这个路标送去指定的位置”,比如第 1 项,就是
“根据第 1 项的 4,把这个第 1 项,也就是当前状态下的第 1 维,送回到第 4 个维度去,即放到 inverseorder 的第 4 项”;
这样,再次调用 permute 时,函数遇到了第 4 项的 1,就会把新数组的第 1 维找来,放在第 4 维的位置,还原了原来数组的维度排列。
或者可以理解成 1:numel(order) 记录了原来每个维度的“名字”或“位置”,第 1 维就叫“1”,或者说就在第“1”个,之后将这个队伍按照 order 来排列,那么新排列的队伍,它们的名字并没有改变,于是将这个新队伍作为排列的依据,自然会找回自己“名字”对应的位置。
明白了这点后,permute 与 ipermute 的作用就明白了,输入数组后,一个是根据数组“找回”,一个是根据数组“送去”,一开始根据一个 order 来“找来”,那么之后按照原来的名单再“送去”,自然是会消除“找来”的影响的。演示一下 ipermute 的“送去”
>> size(a)
ans =
3 4 1 2
>> size(ipermute(a,[4 1 2 3]))
ans =
4 1 2 3
可以看到,第 3 个维度被送到了第 4 个维度上,第 2 个维度被送到了第 1 个维度上,第 3 个维度被送到了第 2 个维度上,第 4 个维度被送到了第 3 个维度上。
permute 与 ipermute 就是这样一对强大的广义转置(维度转换)函数。
关于封面:封面是莎缇拉,因为她有“回溯”的能力,也就是“复原一切”的能力,这个和 ipermute 的原理很相似,虽然干过回档这活的人一大把,但是最近正在看 re0 所以还是放莎缇拉吧。