在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
我非常感谢你在答案中付出的所有努力。
答案 0 :(得分:2)
拒绝方法可能会更快,具体取决于L
和K
的值。
想法是使用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]);
但我认为主要问题是您使用randperm
大K
和小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
。