簡體   English   中英

K個桶中N個對象的所有可能組合

[英]All possible combinations of N objects in K buckets

假設我有 3 個標有 A、B、C 的盒子,我有 2 個球,B1 和 B2。 我想在盒子里得到這些球的所有可能組合。 請注意,知道每個盒子中的球是很重要的,這意味着 B1 和 B2 是不一樣的。

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

編輯

如果有解決這個問題的已知算法,請告訴我它的名字。

N為桶數(示例中為3 ),球數為M2 )。 現在,讓我們看一下示例中[0..N**M) - [0..9)范圍內的數字; 我們用radix = N表示這些數字。 對於問題中的示例,我們有進制數

現在我們可以很容易地解釋這些數字:第一個數字顯示第一個球的位置,第二個 - 第二個球的位置。

      |--- 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`)

一般算法是:

  1. 對於0 .. N**M范圍內的每個數字
  2. i個球 ( i = 0..M-1 ) 將在桶中 # (number / N**i) % N (這里/代表整數除法, %代表余數)

如果你只想要總數,答案很簡單N ** M ,在上面的例子中3 ** 2 == 9

C#代碼算法本身很容易實現:

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;
  }
}

這是可以糾纏的答案表示:

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}"))}"));
  }
}

演示:

  int balls = 3;
  int boxes = 2;

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

  Console.Write(report);

結果:

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

小提琴

有一個非常簡單的遞歸實現,可以在每個級別將當前球添加到每個盒子中。 當所有球都被處理完時,遞歸結束。

這里有一些 Java 代碼來說明。 我們使用Stack來表示每個框,因此我們可以在每個遞歸級別后簡單地彈出最后添加的球。

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();
    }
}

測試:

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);

輸出:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM