Next Composition of n into k parts - does anyone have a working algorithm? [closed]
Asked Answered
B

4

10

Composition of n into k parts - I want to list all the possible compositions of n into k parts - does anyone have an algorithm (preferably in R)? Or know if it's in library anywhere?

For example, if I have n cubes, and k bags, and want to list all the possible arrangements of the cubes in the bags. e.g. there are 3 ways you can arrange 2 cubes into 2 bags:

(2, 0) (1, 1) (0, 2)

I've found the NEXCOM alogarithm. I've found a version of it here (page 46) in Fortran, but don't code in Fortran so really understand what's going on - any help?

Bar answered 10/1, 2011 at 13:4 Comment(0)
T
7

What you are attempting to list is called an k-multicombination. The problem is often stated this way: given n indistinguishable balls and k boxes, list all possible ways to distribute all of the balls in the boxes. The number of such distributions is:

factorial(n + k - 1) / (factorial(k - 1) * factorial(n))

For further background, see Method 4 of the Twelve-Fold Way.

Here is the code to enumerate the distributions (C++):

string & ListMultisets(unsigned au4Boxes, unsigned au4Balls, string & strOut = string ( ), string strBuild = string ( ))
{
    unsigned au4;
    if (au4Boxes > 1) for (au4 = 0; au4 <= au4Balls; au4++)
    {
        stringstream ss;
        ss << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls - au4;
        ListMultisets (au4Boxes - 1, au4, strOut, ss.str ( ));
    }
    else
    {
        stringstream ss;
        ss << "(" << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls << ")\n";
        strOut += ss.str ( );
    }
    return strOut;
}



int main(int argc, char * [])
{    
    cout << endl << ListMultisets (3, 5) << endl;
    return 0;
}

Here is the output from the above program (5 balls distributed over three boxes):

(5,0,0)
(4,1,0)
(4,0,1)
(3,2,0)
(3,1,1)
(3,0,2)
(2,3,0)
(2,2,1)
(2,1,2)
(2,0,3)
(1,4,0)
(1,3,1)
(1,2,2)
(1,1,3)
(1,0,4)
(0,5,0)
(0,4,1)
(0,3,2)
(0,2,3)
(0,1,4)
(0,0,5)
Telford answered 21/1, 2011 at 6:13 Comment(2)
Isn't the equation factorial(n * k * 1) / ( factorial(k - 1) * factorial(n)) ? I'm asking, because I just learned about weak compositions.Pauper
@MattMunson, the equation was correct for an n-multicombination, But I described a k-multicombination in the text. I made another edit to reflect the change.Telford
M
8

Since it took me a bit of effort to read the intention of the other c++ solution here a translation to python (also as generator result instead of string):

def weak_compositions(boxes, balls, parent=tuple()):
  if boxes > 1:
    for i in xrange(balls + 1):
      for x in weak_compositions(boxes - 1, i, parent + (balls - i,)):
        yield x
  else:
    yield parent + (balls,)

test:

>>> for x in weak_compositions(3, 5): print x
(5, 0, 0)
(4, 1, 0)
(4, 0, 1)
(3, 2, 0)
...
(0, 1, 4)
(0, 0, 5)
Merchandising answered 20/4, 2016 at 15:46 Comment(1)
FWIW, I've added Python 3 versions of your code to my answer which uses itertools.combinations to generate weak partitions.Inflect
T
7

What you are attempting to list is called an k-multicombination. The problem is often stated this way: given n indistinguishable balls and k boxes, list all possible ways to distribute all of the balls in the boxes. The number of such distributions is:

factorial(n + k - 1) / (factorial(k - 1) * factorial(n))

For further background, see Method 4 of the Twelve-Fold Way.

Here is the code to enumerate the distributions (C++):

string & ListMultisets(unsigned au4Boxes, unsigned au4Balls, string & strOut = string ( ), string strBuild = string ( ))
{
    unsigned au4;
    if (au4Boxes > 1) for (au4 = 0; au4 <= au4Balls; au4++)
    {
        stringstream ss;
        ss << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls - au4;
        ListMultisets (au4Boxes - 1, au4, strOut, ss.str ( ));
    }
    else
    {
        stringstream ss;
        ss << "(" << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls << ")\n";
        strOut += ss.str ( );
    }
    return strOut;
}



int main(int argc, char * [])
{    
    cout << endl << ListMultisets (3, 5) << endl;
    return 0;
}

Here is the output from the above program (5 balls distributed over three boxes):

(5,0,0)
(4,1,0)
(4,0,1)
(3,2,0)
(3,1,1)
(3,0,2)
(2,3,0)
(2,2,1)
(2,1,2)
(2,0,3)
(1,4,0)
(1,3,1)
(1,2,2)
(1,1,3)
(1,0,4)
(0,5,0)
(0,4,1)
(0,3,2)
(0,2,3)
(0,1,4)
(0,0,5)
Telford answered 21/1, 2011 at 6:13 Comment(2)
Isn't the equation factorial(n * k * 1) / ( factorial(k - 1) * factorial(n)) ? I'm asking, because I just learned about weak compositions.Pauper
@MattMunson, the equation was correct for an n-multicombination, But I described a k-multicombination in the text. I made another edit to reflect the change.Telford
S
1

Computing compositions (I ignore how standard the term is) and combinations is equivalent in a sense. There's a bijective function between combinations of k + 1 in n + k and compositions of n into k parts. All one has to do is assign a number from 1 to n to each letter of the combinations, order the letters according to their number, then:

  • make a tuple composed of the differences between numbers of consecutive letters
  • subtract 1 to each entry of the tuple, and there you have it.

Assuming your algorithm for computing combinations yields combinations with 'ordered letters', then the rest is a trivial computation.

In Python:

from itertools import combinations, tee 
  #see: 
  #http://docs.python.org/library/itertools.html#itertools.combinations
  #http://docs.python.org/library/itertools.html#itertools.tee

def diffed_tuple(t): # return a new tuple but where the entries are the differences # between consecutive entries of the original tuple. #make two iterator objects which yield entries from t in parallel t2, t1 = tee(t) # advance first iterator one step for x in t2: break # return a tuple made of the entries yielded by the iterators return tuple(e2 - e1 for e2, e1 in zip(t2, t1))

# --The Algorithm-- def compositions(n, k): for t in combinations(range(n+k), k+1): # yield the 'diffed tuple' but subtracting 1 from each entry yield tuple(e-1 for e in diffed_tuple(t))

Snippy answered 18/1, 2011 at 21:28 Comment(2)
I am not an expert so I hesitate to edit the answer, but I believe the algorithm given here computes weak compositions, rather than compositions. The difference is that zero is as a possible element of a weak composition, but not of a composition.Kuebbing
Actually, I think this algorithm doesn't work at all. compositions(2, 3) returns a generator over, (0, 0, 0) (0, 0, 1) (0, 1, 0) (1, 0, 0) (0, 0, 0) These are not the compositions of 2, and one of them is repeated!Kuebbing
T
0

I've made the translation of the original NEXCOM algorithm to structured Fortran and Java. The Java version is:

public void allCombinations(final int n, final int k) {
    final int[] input = new int[k + 1];
    Boolean mtc = Boolean.FALSE;
    int t = n;
    int h = 0;
    do {
        if (mtc) {
            if (t > 1) {
                h = 0;
            }
            h++;
            t = input[h];
            input[h] = 0;
            input[1] = t - 1;
            input[h + 1]++;
        } else {
            // First permutation is always n00...0 i.e. it's a descending
            // series lexicographically.
            input[1] = n;
            for (int i = 2; i <= k; i++) {
                input[i] = 0;
            }
        }
        System.out.println(java.util.Arrays.toString(input));
        mtc = input[k] != n;
    } while (mtc);
}
Thyroxine answered 4/5, 2017 at 2:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.