【问题标题】:Assign different arrays to a 3D matrix in Matlab在 Matlab 中将不同的数组分配给 3D 矩阵
【发布时间】:2014-03-19 18:37:37
【问题描述】:

在 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 中运行了几种矩阵大小的所有方法,结果如下:

另外,数字的分布是:

因此,@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

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

【问题讨论】:

  • 看看我的最新编辑。你能测试一下 Luis 的方法和我的方法吗?我现在很好奇它们如何在较新的 MATLAB 版本上进行比较:)
  • 您现在可以在更新后的问题中看到比较。 Luis 的代码似乎更快。无论如何,非常感谢您的时间和兴趣。
  • 感谢您进行比较。请注意,运行时间结果对使用的数字很敏感。例如,如果 K 不够大于 L,我的方法将受到影响
  • 我必须说,我完全搞不懂为什么我的“优化”解决方案总是比原来的,甚至比arrayfun... sort 带有两个参数 randperm?您使用的是哪个版本的 MATLAB?你的硬件是什么?
  • 另外,看看分布,我似乎在 Fisher/Yates 实现中犯了一个错误……你碰巧发现了那个错误吗?

标签: matlab bigdata vectorization


【解决方案1】:

拒绝方法可能更快,具体取决于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]);

【讨论】:

  • 可以对这两种方法做一个小测试吗?我在 R2010a 上,它有一个非内置 randperm 只接受 1 个输入参数...
  • @RodyOldenhuis 我有一个旧版本的randperm (R2010b) :-( 它也只接受一个参数
【解决方案2】:

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

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

但我认为主要问题是您将randperm 与大K 和小L 一起使用。我不确定randperm 是如何在较新版本的 MATLAB(您似乎拥有)上实现的,但如果它与我拥有的版本类似,它会在物理上创建整数的随机排序 1K ,然后从该数组中提取第一个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.

真的还是很令人失望......但是哦,好吧,肯定比以前好多了。

【讨论】:

    【解决方案3】:

    这应该执行得更快:

    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 只接受一个参数,所以我无法尝试您的代码,这种方法适用于arrayfun 中的匿名函数@(x)(randperm(x))

    【讨论】:

    • 算了,'arrayfun' 结果比我预期的要慢。
    • 确实,arrayfun 通常比普通循环慢。顺便说一句:cell2mat 只是一个循环,所以从技术上讲,你仍然有 2 个循环,但 1 隐含在 arrayfun 中,而 1 隐藏在 cell2mat 后面一些开销......所以这确实比两个慢循环。
    • 感谢@RodyOldenhuis,我认为那里涉及更多本机代码。
    • 好吧,问题是匿名函数和 MATLAB 无法内联它。真是可惜了……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 2018-12-23
    • 2016-06-02
    • 2022-06-14
    相关资源
    最近更新 更多