All possible combinations of N objects in K buckets

Viewed 438

Suppose I have 3 boxes labeled A, B, C and I have 2 balls, B1 and B2. I want to get all possible combinations of these balls in the boxes. Please note, it is important to know which ball is in each box, meaning B1 and B2 are not the same.

A         B         C
B1, B2
B1        B2
B1                  B2
B2        B1        
B2                  B1
          B1, B2
          B1        B2
          B2        B1
                    B1, B2

Edit

If there is a known algorithm for this problem, please tell me its name.

2 Answers

Let N be number of buckets (3 in the example), M number of balls (2). Now, let's have a look at numbers in a range [0..N**M) - [0..9) in the example; these numbers we represent with radix = N. For the example in the question we have trinary numbers

Now we can easily interprete these numbers: first digit shows 1st ball location, second - 2nd ball position.

      |--- Second Ball position [0..2]
      ||-- First Ball position  [0..2]  
      ||
  0 = 00 - both balls are in the bucket #0 (`A`)
  1 = 01 - first ball is in the bucket #1 ('B'), second is in the bucket #0 (`A`)
  2 = 02 - first ball is in the bucket #2 ('C'), second is in the bucket #0 (`A`)
  3 = 10 - first ball is in the bucket #0 ('A'), second is in the bucket #1 (`B`)
  4 = 11 - both balls are in the bucket #1 (`B`)
  5 = 12 ...
  6 = 20
  7 = 21 ...
  8 = 22 - both balls are in the bucket #2 (`C`)

the general algorithm is:

  1. For each number in 0 .. N**M range
  2. ith ball (i = 0..M-1) will be in the bucket # (number / N**i) % N (here / stands for integer division, % for remainder)

If you want just total count, the answer is simple N ** M, in the example above 3 ** 2 == 9

C# Code The algorithm itself is easy to implement:

static IEnumerable<int[]> BallsLocations(int boxCount, int ballCount) {
  BigInteger count = BigInteger.Pow(boxCount, ballCount);

  for (BigInteger i = 0; i < count; ++i) {
    int[] balls = new int[ballCount];
    int index = 0;

    for (BigInteger value = i; value > 0; value /= boxCount)
      balls[index++] = (int)(value % boxCount);

    yield return balls;
  }
}

It's answer representation which can be entangled:

static IEnumerable<string> BallsSolutions(int boxCount, int ballCount) {
  foreach (int[] balls in BallsLocations(boxCount, ballCount)) {
    List<int>[] boxes = Enumerable
      .Range(0, boxCount)
      .Select(_ => new List<int>())
      .ToArray();

    for (int j = 0; j < balls.Length; ++j)
      boxes[balls[j]].Add(j + 1);

    yield return string.Join(Environment.NewLine, boxes
      .Select((item, index) => $"Box {index + 1} : {string.Join(", ", item.Select(b => $"B{b}"))}"));
  }
}

Demo:

  int balls = 3;
  int boxes = 2;

  string report = string.Join(
    Environment.NewLine + "------------------" + Environment.NewLine, 
    BallsSolutions(boxes, balls));

  Console.Write(report);

Outcome:

Box 1 : B1, B2, B3
Box 2 : 
------------------
Box 1 : B2, B3
Box 2 : B1
------------------
Box 1 : B1, B3
Box 2 : B2
------------------
Box 1 : B3
Box 2 : B1, B2
------------------
Box 1 : B1, B2
Box 2 : B3
------------------
Box 1 : B2
Box 2 : B1, B3
------------------
Box 1 : B1
Box 2 : B2, B3
------------------
Box 1 : 
Box 2 : B1, B2, B3

Fiddle

There's a very simple recursive implementation that at each level adds the current ball to each box. The recursion ends when all balls have been processed.

Here's some Java code to illustrate. We use a Stack to represent each box so we can simply pop the last-added ball after each level of recursion.

void boxBalls(List<Stack<String>> boxes, String[] balls, int i)
{
    if(i == balls.length)
    {
        System.out.println(boxes);
        return;
    }
    
    for(Stack<String> box : boxes)
    {
        box.push(balls[i]);
        boxBalls(boxes, balls, i+1);
        box.pop();
    }
}

Test:

String[] balls = {"B1", "B2"};
List<Stack<String>> boxes = new ArrayList<>();
for(int i=0; i<3; i++) boxes.add(new Stack<>());

boxBalls(boxes, balls, 0);

Output:

[[B1, B2], [], []]
[[B1], [B2], []]
[[B1], [], [B2]]
[[B2], [B1], []]
[[], [B1, B2], []]
[[], [B1], [B2]]
[[B2], [], [B1]]
[[], [B2], [B1]]
[[], [], [B1, B2]]
Related