Как случайным образом переставить столбцы в 3D матрице в MATLAB - PullRequest
6 голосов
/ 21 июня 2011

У меня есть 3D-матрица (10000 x 60 x 20), и мне нужно переставить 2-е и 3-е измерения, оставив столбцы без изменений.

Для 2D матрицы я использую RANDPERM:

pidx = randperm(size(A,2));
Aperm = A(:,pidx);

Я не могу просто применить RANDPERM дважды - сначала индекс столбца, а затем индекс страницы. Недостаточно рандомизации.

Одним из решений является преобразование матрицы из 3D в 2D, сжимая столбцы и страницы в столбцы, переставляя их, а затем изменяя форму. Но я также хотел бы сделать перестановку таким образом, чтобы столбцы переставлялись независимо для каждой страницы. Что-то вроде:

Aperm = zeros(size(A));
for p=1:size(A,3)
    pidx = randperm(size(A,2));
    Aperm(:,:,p) = A(:,pidx,p);
end

Могу ли я сделать это более эффективно? Есть ли лучшие способы?

Ответы [ 3 ]

3 голосов
/ 22 июня 2011

Решение № 1: столбцы переставляются на всех страницах

изменить форму матрицы с 3D на 2D сжимая столбцы и страницы для столбцы, переставить их, а затем изменить форму назад

A = randi(10, [3 4 2]);                 %# some random 3D matrix

[r c p] = size(A);
Aperm = reshape(A, [r c*p]);
Aperm = reshape(Aperm(:,randperm(c*p)), [r c p]);

Решение № 2: Столбцы переставляются внутри каждой страницы независимо (эквивалент вашего цикла for)

Я также хотел бы сделать перестановку в таким образом, что столбцы переставляются независимо для каждой страницы

A = randi(10, [3 4 2]);                 %# some random 3D matrix

[r c p] = size(A);
Aperm = reshape(A, [r c*p]);

[~,idx] = sort(rand(p,c),2);            %# this is what RANDPERM does
idx = reshape(bsxfun(@plus, idx',0:c:c*(p-1)),1,[]);    %'#

Aperm = reshape(Aperm(:,idx), [r c p]);
3 голосов
/ 22 июня 2011

Вот одно решение, которое достигнет того же результата, что и ваш цикл for (то есть другая перестановка столбцов для каждой страницы):

%# Solution 1:
[r,c,p] = size(A);
Aperm = reshape(A,r,c*p);
index = arrayfun(@randperm,c.*ones(1,p),'UniformOutput',false);
index = [index{:}]+kron(0:c:c*(p-1),ones(1,c));
Aperm = reshape(Aperm(:,index),r,c,p);

И, как и в случае со многими проблемами в MATLAB, существует множество различных способов решения этой проблемы. Вот еще одно решение, которое позволяет избежать изменения формы матрицы, используя только линейное индексирование в A:

%# Solution 2:
[r,c,p] = size(A);
Aperm = zeros([r c p]);
index1 = repmat(1:r,1,c*p);
[~,index2] = sort(rand(c,p));          %# A variation on part of Amro's answer
index2 = kron(index2(:),ones(r,1)).';  %'
index3 = kron(1:p,ones(1,r*c));
index = sub2ind([r c p],index1,index2,index3);
Aperm(:) = A(index);
1 голос
/ 22 июня 2011

Вот решение, аналогичное вашему циклу, но без использования kron, как в ответе gnovice.Хотя я предпочитаю kron, это более безопасная альтернатива, если вы делитесь кодом с людьми, которые не понимают, что kron делает здесь (я говорю из опыта).Однако я должен отметить, что это повлияет на производительность, если pages станет большим (в вашем случае это нормально).

[rows,cols,pages]=size(A);
randCols=arrayfun(@(x)randperm(cols),1:pages,'UniformOutput',false);
Aperm=arrayfun(@(x)A(:,randCols{x},x),1:pages,'UniformOutput',false);
Aperm=cat(3,Aperm{:});
...