在Matlab中为3D矩阵指定不同的阵列

在Matlab中为3D矩阵指定不同的阵列,matlab,bigdata,vectorization,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 然而,当矩阵较大时,计算时间会增加很多。因此,我想删除带有任何矢量化的循环。我想不出有什么办法,有可能吗 谢谢你的帮助 编辑:我已在此中运行了

在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
然而,当矩阵较大时,计算时间会增加很多。因此,我想删除带有任何矢量化的循环。我想不出有什么办法,有可能吗

谢谢你的帮助


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

此外,数字分布如下:

因此,@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
function mat=assignPermMatrix\u comb(M,N,L,K)
R=M*N;
mat=零([L R]);

如果L这应该执行得更快:

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))
,这应该执行得更快:

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

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

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

mat = zeros(L,M*N);
for ii=1:M*N        
    mat(:,ii) = randperm(K,L);
end
mat = reshape(mat.', [M N L]);
但我认为主要的问题是,您正在使用带有大
K
和小
L
randperm
。我不确定新版本的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.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
Elapsed time is 0.001326 seconds.
Elapsed time is 0.001108 seconds.
Elapsed time is 238.300146 seconds.  %// wait, WHAT?!
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.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
Elapsed time is 0.001326 seconds.
Elapsed time is 0.001108 seconds.
Elapsed time is 238.300146 seconds.  %// wait, WHAT?!
然而,
M=10的试验结果;N=10;L=40;K=50

Elapsed time is 0.315532 seconds.
Elapsed time is 0.297795 seconds.
Elapsed time is 0.189210 seconds.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
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.

仍然非常令人失望……但哦,好吧,肯定比以前好。

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

mat = zeros(L,M*N);
for ii=1:M*N        
    mat(:,ii) = randperm(K,L);
end
mat = reshape(mat.', [M N L]);
但我认为主要的问题是,您正在使用带有大
K
和小
L
randperm
。我不确定新版本的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.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
Elapsed time is 0.001326 seconds.
Elapsed time is 0.001108 seconds.
Elapsed time is 238.300146 seconds.  %// wait, WHAT?!
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.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
Elapsed time is 0.001326 seconds.
Elapsed time is 0.001108 seconds.
Elapsed time is 238.300146 seconds.  %// wait, WHAT?!
然而,
M=10的试验结果;N=10;L=40;K=50

Elapsed time is 0.315532 seconds.
Elapsed time is 0.297795 seconds.
Elapsed time is 0.189210 seconds.
Elapsed time is 10.818245 seconds.
Elapsed time is 10.733220 seconds.
Elapsed time is 0.788050 seconds.
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.

仍然非常令人失望……但哦,好吧,肯定比以前好。

你能对这两种方法做一个小测试吗?我在R2010a上,它有一个非内置的
randperm
只接受一个输入参数…@RodyOldenhuis我有一个旧版本的
randperm
(R2010b):-(它也只接受一个参数你能对这两种方法做一个小测试吗?我在R2010a上,它有一个非内置的
randperm
只接受一个输入参数…@RodyO