【问题标题】:List of combinations of N balls in M boxes in C++C++中M个盒子中N个球的组合列表
【发布时间】:2011-06-28 14:46:19
【问题描述】:

我想编写一个函数,生成一个元组数组,其中包含 C++ 中 M 个盒子中 N 个球的所有可能排列。

顺序(编辑:在结果列表中)并不重要,只是第一个必须是 (N,0,...,0),最后一个必须是 (0,0,...,N)。

我没有在网上找到这样的C++实现,只有字符的排列或排列数的计算......

有什么想法吗?

【问题讨论】:

  • 如果我错了,请纠正我,但排列顺序不重要吗?除非我误解了你的意思。
  • 只是为了保持传统,这是功课吗?
  • @MGZero:你是对的; “排列”是错误的术语(但不确定正确的术语是什么)。顺便说一句,该问题对应于生成oooooooo|||| 的所有排列,其中有 N os 和 M-1 |s。
  • 那么正确的术语应该是组合。
  • 对不起,我的意思是在结果列表的元素中顺序并不重要

标签: c++ permutation combinations


【解决方案1】:

有一个巧妙的技巧可以解决这个问题。想象一下,我们把 n 个球和 m − 1 个盒子放在一排长度为 n + m − 1(盒子在球之间混在一起)。然后将每个球放入其右侧的盒子中,并在右侧添加第 m 个盒子,用于接收剩余的所有球。

这会在 m 个盒子中产生 n 个球的排列。

很容易看出 n 个球的排列数量与 m − 1 个盒子(第一张图片)的排列数量相同m 个盒子中有 n 个球。 (走一个方向,把每个球放在右边的盒子里;走另一个方向,每个盒子把球倒到它左边的位置。)第一张图片中的每个排列都是由我们放置的位置决定的盒子。有 m - 1 个框和 n + m - 1 个位置,所以有 n + m - 1Cm - 1 种方法。

所以你只需要一个普通的组合算法(see this question)来生成盒子的可能位置,然后取连续位置之间的差异(减去1)来计算每个盒子里的球数。

在 Python 中这将非常简单,因为标准库中有 combinations 算法:

from itertools import combinations

def balls_in_boxes(n, m):
    """Generate combinations of n balls in m boxes.

    >>> list(balls_in_boxes(4, 2))
    [(0, 4), (1, 3), (2, 2), (3, 1), (4, 0)]
    >>> list(balls_in_boxes(3, 3))
    [(0, 0, 3), (0, 1, 2), (0, 2, 1), (0, 3, 0), (1, 0, 2), (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0), (3, 0, 0)]

    """
    for c in combinations(range(n + m - 1), m - 1):
        yield tuple(b - a - 1 for a, b in zip((-1,) + c, c + (n + m - 1,)))

【讨论】:

    【解决方案2】:

    M 个盒子中 N 个球的组合列表 = k + 从 0 到 N 的每 k 个 (M-1) 个盒子中 (N-k) 个球的组合列表。尝试递归编码。

    【讨论】:

      【解决方案3】:

      您可以使用向量队列递归地解决此问题,其中您有一个带有 for 循环的函数,该函数循环遍历 N 个球的数量,将 N 个球中的每一个放在表示的 M 个盒子中的单个盒子中通过一个大小为 M 的向量。然后它递归地调用相同的函数,但传入一个减少的索引值,用于设置向量中 N 个球的值。递归调用的基本情况将使用大小为 M 的向量初始化队列,并创建 N 个向量,每个向量都有一个初始化槽(在本例中为槽 0),设置有来自 N 个球的单个值。

      编辑: 我已经更改了代码,现在它反映了多重组合,而不是排列。这需要添加一个新的struct box_t,它允许使用正确地将框存储在队列中,并告诉我们何时点击重复。

      struct box_t
      {
          vector<int> boxes;
          int flag;  //this flag lets us know if we're repeating a value
          box_t(int num_boxes): boxes(num_boxes), flag(0) {}
      };
      
      typedef queue<box_t> comb_queue;
      
      comb_queue multi_combinations(int num_boxes, int ball_index)
      {
          if (ball_index == 0)
          {
              comb_queue initial_queue;
      
              //initialize our queue with M vectors that will have 
              //only one of the "boxes" initialized with a value
              for (int i=0; i < num_boxes; i++)
              {
                  box_t temp(num_boxes);
                  temp.boxes[i] += 1;
                  initial_queue.push(temp);
              }
      
              return initial_queue;
          }
      
          //here is our recursive call
          comb_queue box_combinations = multi_combinations(num_boxes, ball_index - 1);
          int queue_size = box_combinations.size();
      
          for (int i=0; i < queue_size; i++)
          {
              box_t boxes = box_combinations.front();
              box_combinations.pop();
      
              if (boxes.flag)
              {
                  //this combination has already been processed, so place back in the queue
                  //and move on
                  box_combinations.push(boxes);
                  continue;
              }
      
              for (int j=0; j < num_boxes; j++)
              {
                   //increment the ball-count in each of the boxes
                   boxes[j] += 1;
                   box_combinations.push(boxes);
      
                   //remove the ball we just added from the box slot for the next loop
                   boxes[j] -= 1;
              }
      
              //finally place the box_t we've been processing back in the queue, but with the
              //repeat flag set
              boxes.flag = 1;
              box_combinations.push(boxes);
          }
      
          return box_combinations;
      }
      

      然后像这样调用函数:

      comb_queue all_multi_combinations = multi_combinations(M, (N-1));
      

      输出现在将是一个向量队列,其中包含 M 个盒子中 N 个球的每个多重组合的每个盒子中的球数。

      【讨论】:

      • 感谢此代码。其实我只需要知道每个盒子里有多少个球,而不是每个盒子里有哪个。我会修改你的例子并发布它。
      • 抱歉,因为您要求排列,我有点困惑,但您似乎真的在寻找所有可能的多重组合......这肯定需要对代码进行一些更改,但是概念仍然与向量队列相同。
      • 顺便说一句,安迪,我已经按照您的要求更新了多组合的代码。
      • 感谢和抱歉我对数学术语的困惑
      【解决方案4】:

      其实有一个。看一看:http://photon.poly.edu/~hbr/boost/combinations.html

      它不是在 boost 中,而是遵循从名称中很容易看出的相同原则。

      【讨论】:

      • 这个组合了一组。就我而言,只有一些球和一些盒子。排列将描述每个盒子中的球数
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多