1

Been trying to solve this simple problem for a while but just couldn't find the solution for the life of me...

I'm programming an experiment in PsychToolbox but I'll spare you the details, basically I have two vectors A and B of equal size with the same number of ones and zeroes:

A = [0 0 1 1]
B = [0 0 1 1]

Both vectors A and B must be randomized independently but in such a way that one combination of items between the two vectors is never repeated. That is, I must end up with this

A = [1 1 0 0]
B = [1 0 0 1]

or this:

A = [0 0 1 1]
B = [0 1 0 1]

but I should never end up with this:

A = [1 1 0 0]
B = [1 1 0 0]

or this

A = [0 1 0 1]
B = [0 1 0 1]

One way to determine this is to check the sum of items between the two vectors A+B, which should always contain only one 2 or only one 0:

A = [1 1 0 0]
B = [1 0 0 1]

A+B = 2 1 0 1

Been trying to make this a condition within a 'while' loop (e.g. so long as the number of zeroes in the vector obtained by A+B is superior to 1, keep randomizing A and B), but either it still produces repeated combination or it just never stops looping. I know this is a trivial problem but I just can't get my head around it somehow. Anyone care to help?

This is a simplified version of the script I got:


A = [1 1 0 0];
B = A;
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);


while nnz(~(A+B)) > 1
    ARand = randperm(length(A));
    A = A(ARand);
    BRand = randperm(length(B));
    B = B(BRand);
end

Still, I end up with repeated combinations.

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
Nic
  • 13
  • 2

3 Answers3

0
% If you are only looking for an answer to this scenario the easiest way is
% as follows:

A = [0 0 1 1];
B = [0 0 1 1];
nn = length(A);

keepset = [0 0 1 1;0 1 0 1];
keepset = keepset(:,randperm(nn))


% If you want a more general solution for arbitrary A & B (for example)
A = [0 0 0 1 1 1 2 2 2];
B = [0 0 0 1 1 1 2 2 2];
nn = length(A);

Ai = A(randperm(nn));
Bi = B(randperm(nn));

% initialize keepset with the first combination of A & B
keepset = [Ai(1);Bi(1)];

loopcnt = 0;
while (size(keepset,2) < nn) 

    % randomize the elements in A and B independently
    Ai = A(randperm(nn));
    Bi = B(randperm(nn));
    % test each combination of Ai and Bi to see if it is already in the
    % keepset
    for ii = 1:nn
        tstcombo = [Ai(ii);Bi(ii)];
        matchtest = bsxfun(@eq,tstcombo,keepset);
        matchind = find((matchtest(1,:) & matchtest(2,:)));
        if isempty(matchind)
            keepset = [keepset tstcombo];
        end
    end
    loopcnt = loopcnt + 1;
    
    if loopcnt > 1000
        disp('Execution halted after 1000 attempts')
        break
    elseif (size(keepset,2) >= nn)
        disp(sprintf('Completed in %0.f iterations',loopcnt))
    end
        
end
keepset
KXO2022
  • 91
  • 6
0

It's much more efficient to permute the combinations randomly than shuffling the arrays independently and handling the inevitable matching A/B elements.

There are lots of ways to generate all possible pairs, see How to generate all pairs from two vectors in MATLAB using vectorised code?

For this example I'll use

allCombs = combvec([0,1],[0,1]);
% = [ 0  1  0  1
%     0  0  1  1 ]

Now you just want to select some amount of unique (non-repeating) columns from this array in a random order. In all of your examples you select all 4 columns. The randperm function is perfect for this, from the docs:

p = randperm(n,k) returns a row vector containing k unique integers selected randomly from 1 to n inclusive.

n = size(allCombs,2); % number of combinations (or columns) to choose from
k = 4;                % number of columns to choose for output
AB = allCombs( :, randperm(n,k) ); % random selection of pairs

If you need this split into two variables then you have

A = AB(1,:);
B = AB(2,:);
Wolfie
  • 27,562
  • 7
  • 28
  • 55
-1

Here's a possible solution:

A = [0 0 1 1];
B = [0 0 1 1];

% Randomize A and B independently
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);

% Keep randomizing A and B until the condition is met
while sum(A+B) ~= 1 && sum(A+B) ~= length(A)
    ARand = randperm(length(A));
    A = A(ARand);
    BRand = randperm(length(B));
    B = B(BRand);
end

This solution checks if the sum of the elements in A+B is either 1 or the length of A, which indicates that only one element in A+B is either a 0 or a 2, respectively. If either of these conditions is not met, the vectors A and B are randomized again.

Unexpected
  • 37
  • 1
  • 2
  • Hi thanks! Nice try but I still end up with missing or doubled combinations :) Very close to what I tried. Thank you for taking the time! – Nic Dec 16 '22 at 15:20