在Matlab中将不同的数组分配给3D矩阵

时间:2014-03-14 12:59:47

标签: matlab bigdata vectorization

在matlab中,我想要一个3D矩阵,其随机数在第3维上是唯一的,如下代码所示:

M = 2;  N = 10;  L = 5;  K = 100;
mat = zeros([M N L]);
for ii=1:M
    for jj=1:N
        mat(ii,jj,:) = randperm(K,L);
    end
end

然而,当矩阵较大时,计算时间会增加很多。因此,我想删除任何矢量化的循环。我无法通过任何方式解决这个问题,有可能吗?

感谢您的帮助。


编辑:我已在此script中运行了几种矩阵大小的所有方法,结果如下:

enter image description here

此外,数字的分布是:

enter image description here

因此, @Luis Mendo 的实现是针对低L值更好地扩展的实现,这是我的情况。但是, @Rody Oldenhuis 优化提案与L值快速相关。因此,综合解决方案可能是:

function mat = assignPermMatrix_comb(M,N,L,K)
    R = M*N;
    mat = zeros([L R]);
    if L<K*0.15
        ind = true(1,R); 
        while R
            mat(:,ind) = randi(K, L, R); 
            ind = any(diff(sort(mat))==0);
            R = nnz(ind);
        end
    else
        for ii=1:R
            mat(:,ii) = randperm(K,L);
        end
    end
    mat = reshape(mat.', [M N L]);
end

我非常感谢你在答案中付出的所有努力。

3 个答案:

答案 0 :(得分:2)

拒绝方法可能会更快,具体取决于LK的值。

想法是使用randi生成所有条目而不考虑重复,检测具有重复的第三条暗淡线,并再次生成这些条目,直到不存在重复。将前两个维度合并为一个更容易,最后重塑。

当然,这种方法的运行时间是随机的。

ind = true(1,M*N); %// lines that need generaring. Initially all of them
R = M*N; %// number of third-dim-lines that need to be generated
while R
    output(:,ind) = randi(K, L, R); %// (re)generate random values where needed 
    ind = any(diff(sort(output))==0); %// detect repetitions, for next iteration
    R = nnz(ind);
end
output = output.';
output = reshape(output, [M N L]);

答案 1 :(得分:2)

部分展开你的循环肯定会有所帮助:

mat = zeros(L,M*N);
for ii=1:M*N        
    mat(:,ii) = randperm(K,L);
end
mat = reshape(mat.', [M N L]);

但我认为主要问题是您使用randpermK和小L。我不确定如何在更新版本的MATLAB(你似乎有)上实现randperm,但如果它与我的版本类似,它会物理地创建整数1的随机排序通过K,然后从该数组中提取第一个L。因此,如果K相对较大且L相对较小,则意味着您在每次循环迭代时都会做很多不必要的工作。路易斯的解决方案更好。

要测试该理论,请考虑以下简单测试:

M = 20;  N = 100;  
L = 5;   K = 1000;

%// Original
tic
mat = zeros([M N L]);
for ii=1:M
    for jj=1:N   
        [~,P] = sort(rand(K,1)); %// Note: I don't have the 
        mat(ii,jj,:) = P(1:L);   %// newer randperm
    end
end
toc

%// Optimized version
tic
mat = zeros(L, M*N);
for ii=1:M*N
    [~,P] = sort(rand(K,1));
    mat(:,ii) = P(1:L);
end
mat = reshape(mat.', [M N L]);
toc

%// Avoid doing so much useless work
tic
ints = 1:K;
mat = zeros(L, M*N);
for ii=1:M*N
    mat(:,ii) = inds(randi(K,L,1));
end
mat = reshape(mat.', [M N L]);
toc

结果:

Elapsed time is 0.233492 seconds. %// original
Elapsed time is 0.231393 seconds. %// optimized
Elapsed time is 0.007062 seconds. %// oh...wow.

请注意,上一次测试尚未是一个有效的解决方案,因为我还没有检查唯一性。然而,它表明这可能仍然是较新的randperm正在发挥作用的方式。

所以,最终版本:

ints = 1:K;
mat = zeros(L, M*N);
for ii=1:M*N
    inds = randi(K,L,1);
    while any(diff(sort(inds))==0)
        inds = randi(K,L,1); end
    mat(:,ii) = inds();
end
mat = reshape(mat.', [M N L]);

M = 100; N = 200; L = 5; K = 100;的测试结果:

Elapsed time is 0.315532 seconds.
Elapsed time is 0.297795 seconds.
Elapsed time is 0.189210 seconds.

M = 100; N = 200; L = 5; K = 100;的测试结果:

Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.

然而,M = 10; N = 10; L = 40; K = 50;的测试结果:

Elapsed time is 0.001326 seconds.
Elapsed time is 0.001108 seconds.
Elapsed time is 238.300146 seconds.  %// wait, WHAT?!

所以,我们似乎必须想出更聪明的东西......

所以,经过一些灵魂搜索后,我想出了以下内容:

%// This uses a form of the Fisher/Yates shuffle algorithm
mat  = zeros(L, M*N);
ints = 1:K;
inds = randi(K,M*N,L);
L1   = 1:L;

for ii = 1:M*N

    tmp = ints(L1);
    ints(L1) = ints(inds(ii,:));
    ints(inds(ii,:)) = tmp;

    mat(:,ii) = ints(L1);

end

mat = reshape(mat.', [M N L]);

M = 250; N = 250; L = 150; K = 250;

的结果
Elapsed time is 2.332690 seconds.
Elapsed time is 2.140191 seconds.
Elapsed time is 1.512606 seconds.

M = 250; N = 250; L = 15; K = 100;

的结果
Elapsed time is 1.021733 seconds.
Elapsed time is 0.956033 seconds.
Elapsed time is 0.445112 seconds.

真的很令人失望......但是哦,当然比它更好。

答案 2 :(得分:0)

这应该更快地执行:

s = repmat(L, [M*N 1]);
P = arrayfun(@(x)(randperm(K, x)), s, 'UniformOutput', false);
Q = cell2mat(P);
mat = reshape(Q, [M N L]);

注意randperm我只接受一个参数,因此我无法尝试您的代码,这种方法适用于匿名函数@(x)(randperm(x))arrayfun