索引到N-D矩阵

时间:2015-08-16 18:29:20

标签: arrays matlab

我有一个矩阵M(4 * 2),值为:

[1 0;
 0 0;
 1 1;
 0 1]

数组X = [0.3 0.4 0.5 0.2];

M的所有列条目都是二进制(0/1)。我想要的是映射到名为[2,2]的{​​{1}}的ND阵列的相应行值。这里的每个维度表示0/1,通过将其放在第一行或第二行中。 Z需要转到X(1)Z(2,1)需要转到X(2)等等。

Z将如下所示:

Z(1,1)

目前我正在循环使用它,但这样做真的很贵。请注意,这是一个最小的例子 - 我需要将这个128 * 7矩阵转换为7D阵列。

有关如何加快此过程的任何建议?

3 个答案:

答案 0 :(得分:2)

您可以尝试使用accumarray(不确定它是否更快):

>> Z = accumarray(M + 1, X, [2 2])

Z =

    0.4000    0.2000
    0.2000    0.5000

答案 1 :(得分:1)

怎么样

D=[1 0;0 0;1 1;0 1]; X = [0.3 0.4 0.5 0.2];

Z(sub2ind([2 2], D(:, 1) + 1, D(:, 2) + 1)) = X;
Z = reshape(Z, 2, 2);

修改

不幸的是,sub2ind的大部分开销都是错误检查。如果您觉得所有D值都在范围内,则可以有效地内联sub2ind操作。这是一个例子:

ndx = M(:, 1) + 1;
ndx = ndx + M(:, 2) * 2;
Z2=[];
Z2(ndx) = X;
Z2 = reshape(Z2, 2, 2);

在@ johnnyfuego的答案中使用此代码和时间测试,我得到了

Elapsed time is 0.154196 seconds.    <--- johnny's
Elapsed time is 0.288680 seconds.    <--- mine
Elapsed time is 0.143874 seconds.

所以,更好,但仍未击败其他两个。但请注意,这里有一个收支平衡点。如果我将时间测试中的设置代码更改为

M = randi(2, 1000, 2) - 1;
X = rand(1, 1000);

也就是说,我将要写入的值的数量从4增加到1000,然后计时器结果为

Elapsed time is 3.650833 seconds.     <--- johnny's
Elapsed time is 0.607361 seconds.     <--- mine
Elapsed time is 0.872595 seconds.

编辑#2

以下是展开多维sub2ind的方法:

siz = [2 2 2 2 2 2 2];
offsets = cumprod(siz);
ndx = M(:, 1) + 1;
ndx = ndx + M(:, 2) * offsets(1);
ndx = ndx + M(:, 3) * offsets(2);
ndx = ndx + M(:, 4) * offsets(3);
ndx = ndx + M(:, 5) * offsets(4);
ndx = ndx + M(:, 6) * offsets(5);
ndx = ndx + M(:, 7) * offsets(6);
Z2=[];
Z2(ndx) = X;
Z2 = reshape(Z2, [siz]);

在更新的时间测试中使用它我得到:

Elapsed time is 43.754363 seconds.
Elapsed time is 1.045980 seconds.
Elapsed time is 0.689487 seconds.

所以,仍然比循环更好,但看起来在这个多维案例中,accumarray(Rafael的回答)获胜。我会考虑给他“接受的答案”。

答案 2 :(得分:0)

谢谢@ rafael-monteiro&amp; @SCFRench。

我原来的程序更快。我在下面粘贴了一个基准测试脚本。

M=[1 0; 0 0; 1 1; 0 1];

X = [0.3 0.4 0.5 0.2];

nrep=50000;


%% My own code
tic
for A=1:nrep;
    MN=M+1; % I know I can do this outside of the loop, but comparison with this seems more fair.
    Z=zeros(size(X,2)/2,size(X,2)/2); % without pre-allocation it is twice as fast, I guess finding the size and the computation does not help here!
    for I=1:4
        Z1(MN(I,1),MN(I,2))=X(I);
    end
end
toc

%% SCFrench code
tic
for A=1:nrep;
    Z2(sub2ind([2 2], M(:, 1) + 1, M(:, 2) + 1)) = X;
    Z2 = reshape(Z2, 2, 2);
end
toc

%% Rafael code
tic
for A=1:nrep;
    Z3 = accumarray(M + 1, X, [2 2]);
end
toc

Elapsed time is 0.115488 seconds. % mine
Elapsed time is 1.082505 seconds. % SCFrench
Elapsed time is 0.282693 seconds. % rafael

编辑:

使用更大的数据,第一个实现似乎要慢得多。

alts=7;
M = dec2bin(0:2^alts-1)-'0';  
X = rand(size(M,1),1);
nrep=50000;

tic
for A=1:nrep;
    MN=M+1;
    for I=1:128
        Z1(MN(I,1),MN(I,2),MN(I,3),MN(I,4),MN(I,5),MN(I,6),MN(I,7))=X(I);
    end
end
toc

tic
for A=1:nrep;
    Z2(sub2ind([2 2 2 2 2 2 2], M(:, 1) + 1, M(:, 2) + 1, M(:, 3) + 1, M(:, 4)     + 1, M(:, 5) + 1, M(:, 6) + 1, M(:, 7) + 1)) = X;
    Z2 = reshape(Z2, [2 2 2 2 2 2 2]);
end
toc

tic
for A=1:nrep;
    Z3 = accumarray(M + 1, X, [2 2 2 2 2 2 2]);
end
toc

Elapsed time is 33.390247 seconds. % Mine
Elapsed time is 4.280668 seconds.  % SCFrench
Elapsed time is 0.629584 seconds.  % Rafael