6

I have 3D matrix (10000 x 60 x 20) and I need to permute 2nd and 3rd dimensions keeping the columns intact.

For 2D matrix I use RANDPERM:

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

I cannot just apply RANDPERM twice - column index first, then page index. Not enough randomization.

One solution is to reshape the matrix from 3D to 2D squeezing columns and pages to columns, permute them and then reshape back. But I'd also like to do permutation in such a way that columns permuted independently for each page. Something like:

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

Can I do it more efficiently? Any better ways?

gnovice
  • 125,304
  • 15
  • 256
  • 359
yuk
  • 19,098
  • 13
  • 68
  • 99
  • so each of A(i,:,:) needs to be randomly permuted? And each column needs to be permuted independently? A(i,randperm(size(A,2)),randperm(size(A,3))? Then I suppose it depends on how much randomness you really need. If each A(i,:,:) must be permuted completely independently from each A(j,:,:), then I don't think you can do better than looping over the fist index, computing two randperms, and applying them. If less randomization is ok, you might be able to get away with performing a circular shift on one randperm, or a small set of them. – BlessedKey Jun 21 '11 at 19:54
  • 1
    @BlessedKey: Actually it's opposite. Every `A(i,:,:)` need to be permuted the same way. This is what I mean 'keeping columns intact'. `A(i,randperm(size(A,2)),randperm(size(A,3))` does not give me enough permutation. It's basically permutation of page index inside column index. – yuk Jun 21 '11 at 20:02
  • @yuk: What do you mean by "not enough randomization"? – abcd Jun 21 '11 at 20:06
  • @yuk: Another Q: Do you want the columns shuffled between the pages too? Or within each page, shuffle the columns? – abcd Jun 21 '11 at 20:08
  • @yoda: It's if I do `A(i,randperm(size(A,2)),randperm(size(A,3))`. All columns and pages permuted the same way. To better understand my point try `x = reshape(1:24,4,6); x(randperm(4),randperm(6))` – yuk Jun 21 '11 at 20:40
  • @yuk: I _think_ I understand what you want, in which case, Amro's answer below is what you will need. – abcd Jun 21 '11 at 20:52
  • @yoda: On columns shuffling between pages. Yes, first I thought about it. It's what I tried to describe as one solution, which considers all columns independent (@Amro's answer exactly). But then I decided to keep the columns inside their page. For my particular case. – yuk Jun 21 '11 at 20:55
  • @yoda: Why you've deleted the answer? I think it's worth being here, at least for education purpose. – yuk Jun 21 '11 at 21:10
  • @yuk: I deleted it initially because the second part of Amro's answer basically covers it. But I undeleted it now, because I think it's easier for a beginner to follow that than worry with linear indices, although I'd choose the latter if I were to do it. – abcd Jun 21 '11 at 21:15

3 Answers3

3

Here's one solution that will achieve the same result as your for loop (i.e. a different permutation of columns for each page):

%# 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);

And, as with many problems in MATLAB, there are a number of different ways to solve this. Here's another solution that avoids reshaping of the matrix by using only linear indexing into 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);
gnovice
  • 125,304
  • 15
  • 256
  • 359
3

Solution#1: Columns are permuted across all pages

reshape the matrix from 3D to 2D squeezing columns and pages to columns, permute them and then reshape back

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]);

Solution#2: Columns are permuted within each pages independently (equivalent to your for-loop)

I'd also like to do permutation in such a way that columns permuted independently for each page

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]);
Amro
  • 123,847
  • 25
  • 243
  • 454
1

Here's a solution that's the same as your loop, but without using kron as in gnovice's answer. Although I prefer kron, this is a safer alternative if you happen to be sharing code with people who won't understand what kron does here (I speak from experience). I must however note that this will affect performance if pages becomes large (in your case, it is ok).

[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{:});
abcd
  • 41,765
  • 7
  • 81
  • 98