49

More than once now I have needed to generate all possible pairs of two vectors in MATLAB which I do with for loops which take up a fair few lines of code i.e.

vec1 = 1:4;
vec2 = 1:3;
i = 0;
pairs = zeros([4*3 2]);
for val1 = vec1
    for val2 = vec2
         i = i + 1;
         pairs(i,1) = val1;
         pairs(i,2) = val2;
    end
end

Generates ...

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1 
4 2
4 3

There must be a better way to do this which is more MATLAB'esque?

n.b. nchoosek does not do the reversed pairs which is what I need (i.e. 2 1 as well as 1 2), I can't just reverse and append the nchoosek output because the symmetric pairs will be included twice.

Brendan
  • 18,771
  • 17
  • 83
  • 114
  • 2
    possible duplicate of [Matlab - Generate all possible combinations of the elements of some vectors](http://stackoverflow.com/questions/4165859/matlab-generate-all-possible-combinations-of-the-elements-of-some-vectors) – Amro Sep 16 '11 at 15:52

9 Answers9

97

Try

[p,q] = meshgrid(vec1, vec2);
pairs = [p(:) q(:)];

See the MESHGRID documentation. Although this is not exactly what that function is for, but if you squint at it funny, what you are asking for is exactly what it does.

Lambdageek
  • 12,465
  • 1
  • 25
  • 33
  • 2
    That's brilliant. I knew both `meshgrid` and serialising of 2d vectors but never considered using them in this way. – Brendan Sep 17 '11 at 03:00
13

You may use

a = 1:4;
b = 1:3;
result = combvec(a,b);
result = result'
efirvida
  • 131
  • 1
  • 3
  • 1
    Note that this requires the Neural Network toolbox. For those who have it it seems to be the best solution. – Dennis Jaheruddin May 12 '14 at 15:43
  • Like this better than the accepted solution, as it generalizes to multiple vectors. – eric Feb 05 '15 at 18:14
  • 2
    @neuronet A late reply I know, but it's worth future visitors knowing that this is *very* slow compared to the accepted solution for large vectors. – Wolfie Jul 28 '17 at 15:45
4

You could do it by replicating the matrices using repmat and then turning the result into a column vector using reshape.

a = 1:4;
b = 1:3;
c = reshape( repmat(a, numel(b), 1), numel(a) * numel(b), 1 );
d = repmat(b(:), length(a), 1);
e = [c d]

e =

     1     1
     1     2
     1     3
     2     1
     2     2
     2     3
     3     1
     3     2
     3     3
     4     1
     4     2
     4     3

Of course, you can get rid of all the intermediate variables from the example above.

sum1stolemyname
  • 4,506
  • 3
  • 26
  • 44
Praetorian
  • 106,671
  • 19
  • 240
  • 328
3

Another solution for collection:

[idx2, idx1] = find(true(numel(vec2),numel(vec1)));
pairs = [reshape(vec1(idx1), [], 1), reshape(vec2(idx2), [], 1)];
Eitan T
  • 32,660
  • 14
  • 72
  • 109
yuk
  • 19,098
  • 13
  • 68
  • 99
  • +1: I've deleted my solution, I like yours better. I've taken the liberty to correct it though, to obtain the combinations of `vec1` and `vec2`. – Eitan T Apr 10 '13 at 17:30
  • @EitanT: No need to reshape. Indices from `find` always should be column vectors, as well as `vec1(idx1)` etc. – yuk Apr 10 '13 at 17:51
  • I think that reshaping is necessary. Given that `idx1` is a vector (doesn't matter if row or column), if `vec1` is a row vector then `vec1(idx1)` will also be a row vector. The same goes for `vec2`. – Eitan T Apr 10 '13 at 18:12
2

What you are looking for is the cartesian product

cartprod is the function that implements it. You can find it in the linear-algebra package so you would need to do:

   >> pkg install -forge linear-algebra
   >> pkg load linear-algebra 
   >> sortrows(cartprod(1:4,1:3))                                            
    ans =                                                                                           
       1   1                                                                  
       1   2                                                                  
       1   3                                                                  
       2   1                                                                  
       2   2                                                                  
       2   3                                                                  
       3   1                                                                  
       3   2                                                                  
       3   3                                                                  
       4   1                                                                  
       4   2                                                                  
       4   3    
elviejo79
  • 4,592
  • 2
  • 32
  • 35
  • For what it's worth, a function claiming to do the same thing is also available on the [MATLAB File exchange](http://www.mathworks.com/matlabcentral/fileexchange/5475-cartprod--cartesian-product-of-multiple-sets). – mikkola Apr 11 '16 at 19:36
2

You can use plain old matrix operations, e.g. in

x = [3,2,1];
y = [11,22,33,44,55];
v = [(ones(length(y),1) * x)(:), (ones(length(x), 1) * y)'(:)]

Edit: this is Octave syntax, MATLAB will look like this:

x = [3,2,1];
y = [11,22,33,44,55];
A = ones(length(y),1) * x;
B = (ones(length(x), 1) * y)';
v = [A(:) B(:)]

in both cases, result will be

v =
 3    11
 3    22
 3    33
 3    44
 3    55
 2    11
 2    22
 2    33
 2    44
 2    55
 1    11
 1    22
 1    33
 1    44
 1    55
jihor
  • 2,478
  • 14
  • 28
2

As of MATLAB R2023a, you can use the combinations function to do this. Details at https://blogs.mathworks.com/matlab/2023/04/04/the-new-combinations-function-in-matlab-for-cartesian-products-and-parameter-sweeps/

> A = [1,2,3];B=[4,5];
>> C = combinations(A,B)
C =
6×2 table
A    B
_    _
1    4
1    5
2    4
2    5
3    4
3    5

The result is a table. When all data types are compatible (as is the case here) you can get the matrix like this

>> C.Variables
ans =
1     4
1     5
2     4
2     5
3     4
3     5
WalkingRandomly
  • 4,537
  • 5
  • 34
  • 34
1

Here a more MATLAB'esque way to find the combinations. This one can also be easily extended to more then 2 vectors (and also non-numerical combinations):

v1 =   1:  1:  3;
v2 =  11: 11: 44;
v3 = 111:111:555;

dimensions = cellfun(@numel, {v1,v2,v3});

[i1,i2,i3] = ind2sub(dimensions, 1:prod(dimensions));

combinations = [v1(i1); v2(i2); v3(i3)]'
pAtrick
  • 96
  • 2
1

Starting from version R2015a, you can do this using repelem and repmat:

>> vec1 = 1:4;
>> vec2 = 1:3;
>> pairs = [repelem(vec1(:), numel(vec2)) ...
            repmat(vec2(:), numel(vec1), 1)]

pairs =

     1     1
     1     2
     1     3
     2     1
     2     2
     2     3
     3     1
     3     2
     3     3
     4     1
     4     2
     4     3

This type of solution avoids the additional intermediate variables required by some of the other solutions (such as those based on meshgrid) which could lead to memory issues for larger vectors.

gnovice
  • 125,304
  • 15
  • 256
  • 359