Sorting and randomising a matrix according to a particular constraint.

3 views (last 30 days)
Hi There,
I have a 32 x 3 matrix as follows:
A =
[ 1 1 1;
1 1 2;
1 1 3;
1 1 4;
1 1 5;
1 1 6;
1 1 7;
1 1 8;
1 2 1;
1 2 2;
1 2 3;
1 2 4;
1 2 5;
1 2 6;
1 2 7;
1 2 8;
2 1 1;
2 1 2;
2 1 3;
2 1 4;
2 1 5;
2 1 6;
2 1 7;
2 1 8;
2 2 1;
2 2 2;
2 2 3;
2 2 4;
2 2 5;
2 2 6;
2 2 7;
2 2 8]
What I need to do is randomise the order of the rows, while keeping the row values together, however, this needs to be constrained such that for A(:, 4), every 8 rows contain only the numbers 1 - 8. So for example, you could have something like:
A(1:8, :) =
[ 1 2 4;
1 1 5;
2 1 6;
1 1 8;
2 2 1;
2 1 2;
2 1 7;
1 1 3]
The occurrence of 1 and 2 in the first two columns needs to be random, 1 - 8 needs to be randomised for every 8 values of the third column. Initially I tried to use the function randswap within a loop with an added constraint, but this has only led to an infinite loop. Also important that the rows stay together as the 1s and 2s in the first two columns need to appear alongside the last column an equal number of times. Pretty stumped. Any help much appreciated.

Accepted Answer

Lauren Shone
Lauren Shone on 29 Feb 2012
Someone has kindly provided this very excellent solution that eradicates a lot of work and gives the perfect solution if anyone is interested:
%# calculate index for the 1's and 2's
r = rand(8,4);
[~,idx12] = sort(r,2);
%# calculate index for the 1's through 8's
[~,idx8] = sort(r,1);
%# use idx8 to shuffle idx12
idx8into12 = bsxfun(@plus,idx8,[0 8 16 24]);
%# now we can construct the output matrix
B = [1 1;1 2;2 1;2 2];
out = [B(idx12(idx8into12),:),idx8(:)];

More Answers (2)

Andrei Bobrov
Andrei Bobrov on 28 Feb 2012
variant
m = size(A,1);
n = 1:8;
out = [1 1 1];
i2 = 1;
while ~all(ismember(n,out(:,3))) && i2 < 100
i1 = randperm(m);
out = A(i1(n),:);
i2 = i2 + 1;
end
more variant
eg
A =[ 1 1 1
1 1 2
1 1 3
1 1 4
1 1 6
1 1 8
1 2 1
1 2 3
1 2 6
1 2 8
2 1 2
2 1 3
2 1 4
2 1 5
2 1 7
2 1 8
2 2 2
2 2 3
2 2 5
2 2 6
2 2 7
2 2 8]
[i1,i1] = sort(A(:,3));
k = mat2cell(A(i1,:),histc(A(:,3),unique(A(:,3))),size(A,2));
out = cell2mat(arrayfun(...
@(i3)k{i3}(randi(size(k{i3},1)),:),randperm(numel(k))','un',0))
more variant for your case
A1 = sortrows(A,3);
out = A1(4*(randperm(8)-1)+randi(4,1,8),:)
last variant
d = rand(8,4);
[~,i1] = sort(d(:,1),1);
[~,i2] = sort(d,2);
A1 = sortrows(A,3);
out = A1(4*(i1-1)+i2(:,1),:)
  3 Comments
Lauren Shone
Lauren Shone on 26 Apr 2012
awesome! Thanks, you answer is actually bit more useful and flexible (for my limited capabilities), many thanks :)

Sign in to comment.


Lauren Shone
Lauren Shone on 29 Feb 2012
I thought maybe I could add a loop like this:
ng = true; while ng ng = false;
X = randswap(trialmat, 1);
for i = 1:32
if X(i, 3) ~= O_All(i)
ng = true;
end
end
end
where O_All is a 32x1 with repeating values of 1-8 every 8 rows in a random order, however this leads to an infinite loop.

Categories

Find more on Operators and Elementary Operations in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!