Ash
Ash

Reputation: 328

combinations totaling to sum

How would one go about generating a matrix with all the possible combinations of number totaling to a sum with repetition?

Basically, combinations of x1, x2, x3 such that x1 + x2 + x3 = n.

For example: n =3

0 1 2 
0 2 1
1 0 2
1 2 0
1 1 1

Is there simple way of doing this using predefined Matlab functions?

I tried

n=6;
nchoosek(0:n,3)

which gives me

 0     1     2
 0     1     3
 0     1     4
 0     1     5
 0     1     6
 0     2     3
 0     2     4
 0     2     5
 0     2     6
 0     3     4
 0     3     5
 0     3     6
 0     4     5
 0     4     6
 0     5     6
 1     2     3
 1     2     4
 1     2     5
 1     2     6
 1     3     4
 1     3     5
 1     3     6
 1     4     5
 1     4     6
 1     5     6
 2     3     4
 2     3     5
 2     3     6
 2     4     5
 2     4     6
 2     5     6
 3     4     5
 3     4     6
 3     5     6
 4     5     6

How would one extract all rows that have the total equal to n? I think linear indexing or find should make it possible, but I don't know how to go about that.

Regards

Upvotes: 5

Views: 2827

Answers (5)

noman pouigt
noman pouigt

Reputation: 976

As @Mark Dickinson pointed out. I just wanted to give intuitive explanation.

Your problem can be restated as "How many ways can you distribute 3 apples to 3 people"?

Suppose you have 3 people: AA, BB, CC and you want to distribute 3 apple among them. How can you do it?

AA   |    BB     | CC
***  |           | 
*    |     *     | *
**   |     *     | 
........
* -> represent apples.

Now if you think the problem is just selecting two | among 5 positions which we know can be done using 5 choose 2.

Upvotes: 0

user4579530
user4579530

Reputation:

The function nchoosek provides the possible ways of selecting r-1 plus signs in the sum. For example, x1 + x2 + x3 = 5, then two signals must be selected over the sum 1 + 1 + 1 + 1 + 1 = 5. For nonnegative solutions make the transformation yi = x1 + 1, and solves y1+y2+ ... = m + n

The result of nchoosek provides the position of the plus sign.

The remaining of program provides the sum between selected signals.

clear all    
close all    
clc    

% Program that generates the possible distributions
% M objects in r boxes 
% Éderson D'Martin Costa    
% 12/02/2015    

% number of objects
m = 3;    

% number of boxes    
r = 3;

% total number of possibilities
% C = nchoosek(m+r-1,r-1)

v = 1:m+r-1;    
C = nchoosek(v,r-1);    
[l,c] = size(C);    
Y = zeros(l,c+1);    
Y(:,1) = C(:,1);    
Y(:,end) = m+r-C(:,end);

for i = r-1:-1:2    
    Y(:,i) = C(:,i)-C(:,i-1);    
end    

X = Y-1;    
display(X)    
% sum(X,2)

Upvotes: 0

Mr.Wizard
Mr.Wizard

Reputation: 24336

I believe you are describing permutations of restricted integer partitions, though your example doesn't seem complete. For n=3 into three parts from elements {0, 1, 2} there are two solutions: {0, 1, 2} and {1, 1, 1}. These may further be permuted into:

{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}, {1, 1, 1}}

If this is not what you want you should clarify why these additional orderings are not to be included.

If this understanding is correct you should be able to find a number of resources by searching for that phrase. Some examples:

Elegant Python code for Integer Partitioning

Integer Partition in Java

Algorithm for generating integer partitions

Integer Partition Algorithm by Jerome Kelleher

Integer Partition Algorithm by Daniel Scocco

Fast Algorithms for Generating Integer Partitions (PDF) (looks heavy-duty)

Stony Brook Algorithm Repository - Partitions

As a practical example, using Mathematica one would write:

IntegerPartitions[6, {3}, Range[0, 5]]

Output:

{{5, 1, 0}, {4, 2, 0}, {4, 1, 1}, {3, 3, 0}, {3, 2, 1}, {2, 2, 2}}

These could each then be permuted to produce the other orderings.

It is quite easy to set up a recursive function to generate these partitions by starting with all numbers <= n, then appending values that are <= that value that do not exceed n. This is continued until each list is length p; if the list totals n it is kept; if it does not it is discarded. Again, in Mathematica:

f[n_, p_, c__] /; Length@{c} == p := If[+c == n, {{c}}, {}]
f[n_, p_, c___] := Array[f[n, p, c, #] &, Min[c, n - +c] + 1, 0, Join]

f[6, 3]

Output:

{{2, 2, 2}, {3, 2, 1}, {3, 3, 0}, {4, 1, 1}, {4, 2, 0}, {5, 1, 0}, {6, 0, 0}}

Upvotes: 0

Mark Dickinson
Mark Dickinson

Reputation: 30581

For concreteness, let's go with your example of 3 values adding up to 6. The standard way to do this is to think of placing 2 'dividers' into a row of 6 identical 'objects': those dividers then divide the objects into 3 groups, and you can read off the length of each group. So all we need to do is enumerate all ways of placing those dividers. You can use nchoosek(1:8, 2) for this: each row of that matrix describes a division, by describing the positions of the 2 dividers amongst the 2 + 6 == 8 objects + dividers.

This is a more efficient approach than enumerating all triples of integers 0-6 and then picking out those that add to the correct total.

I don't really speak MATLAB, so the following is probably unidiomatic (and suggestions to improve it are welcome!), but something like this should work:

% Total we're aiming for.                                                             
n = 6;                                                                                
% Number of pieces to divide that total into.                                         
k = 3;                                                                                
% All possible placements of internal dividers.                                       
dividers = nchoosek(1:(n+k-1), k-1);                                                  
ndividers = size(dividers, 1);                                                        
% Add dividers at the beginning and end.                                              
b = cat(2, zeros(ndividers, 1), dividers, (n+k)*ones(ndividers, 1));                  
% Find distances between dividers.                                                    
c = diff(b, 1, 2) - 1

And here are the results, as provided by this site:

c =

   0   0   6
   0   1   5
   0   2   4
   0   3   3
   0   4   2
   0   5   1
   0   6   0
   1   0   5
   1   1   4
   1   2   3
   1   3   2
   1   4   1
   1   5   0
   2   0   4
   2   1   3
   2   2   2
   2   3   1
   2   4   0
   3   0   3
   3   1   2
   3   2   1
   3   3   0
   4   0   2
   4   1   1
   4   2   0
   5   0   1
   5   1   0
   6   0   0

Upvotes: 8

Luis Mendo
Luis Mendo

Reputation: 112749

Use dec2base to generate all combinations with repetition, and logical indexing to keep only those with the desired sum:

n = 6;
m = 3;
c = dec2base(0:(n+1)^m-1,n+1,m)-'0'; %// generate combinations with repetition
result = c(sum(c,2)==n,:); %// keep those with desired sum. Logical indexing

Upvotes: 1

Related Questions