如何生成分配球到箱子的所有组合,包括空分配和满分配?

How to generate all combinations of assigning balls into bins including empty and full assignments?

这似乎是一个已知问题,但我找不到它。我有 m 个球和 n 个垃圾箱。我想生成将球放入箱子的所有可能组合,包括满分配和空分配,即一个箱子可能是空的或可能包含所有球(请参见下面的示例)。

例如,对于 m=n=2,我发现:

1    x    bin 1 contains ball 1, bin 2 contains nothing.
x    1    bin 1 contains nothing, bin 2 contains ball 1.
2    x    bin 1 contains ball 2, bin 2 contains nothing.
x    2    bin 1 contains nothing, bin 2 contains ball 2.
1,2  x    bin 1 contains balls 1 and 2, bin 2 contains nothing.
x    1,2  bin 1 contains nothing, bin 2 contains balls 1 and 2.
1    2    bin 1 contains ball 1, bin 2 contains ball 2.
2    1    bin 1 contains ball 2, bin 2 contains ball 1.
x    x    bin 1 contains nothing, bin 2 contains nothing.

如何在 Python 中生成这些组合(例如)?您可以提供伪代码或您想要的任何编程语言。

我首先生成所有组合,例如

x
1
2
1,2

然后我想把这些组合分配给垃圾箱,但我无法完成。

好的,这是两个箱子的有限解决方案,但是任意数量的(可区分的)球。

首先要找到可以放入 bin 1 (combsBin1) 的球的可能组合。从那里开始,迭代所有这些组合,并通过排除那些已经放入 bin 1 的球来生成球子集。然后对于 combsBin1 中的每个组合,找到填充 bin 2 的所有组合。最后,这些组合的组合(哇,我很抱歉措辞不佳)被存储。

这是一些 Octave(MATLAB 兼容)代码。这绝对不漂亮,可能有助于删除一些分号以检查中间结果。

balls = [NaN 1 2];
bins = [1 2];

finalCombs = {};

% Fill bin 1
combsBin1 = {};
for iBall = 1:numel(balls)-1
  if (iBall == 1)
    temp = nchoosek(balls, iBall);
  else
    temp = nchoosek(balls(2:end), iBall);
  end
  combsBin1 = [combsBin1; mat2cell(temp, ones(size(temp, 1), 1))];
end

% Output
printf('Possible combinations for bin 1 only:\n\n');
printf('Bin 1\n');
printf('-----\n');
for iC = 1:size(combsBin1, 1)
  printf('%s\n', num2str(combsBin1{iC, 1}));
end

% Fill bin 2 by iterating all combinations found for bin 1
% and excluding all balls which are already in bin 1.
for iComb = 1:numel(combsBin1)
  b = [NaN setdiff(balls(2:end), combsBin1{iComb})];
  if (isnan(b))
    finalCombs = [finalCombs; [combsBin1{iComb}, {NaN}]];
  end
  for iBall = 1:numel(b)-1
    if (iBall == 1)
      temp = nchoosek(b, iBall);
    else
      temp = nchoosek(b(2:end), iBall);
    end
    c = mat2cell(temp, ones(size(temp, 1), 1));
    for iC = 1:numel(c)      
      finalCombs = [finalCombs; [combsBin1{iComb}, c(iC)]];
    end
  end
end

% Output
printf('\n\nPossible combinations for bin 1 and bin 2:\n\n');
printf('Bin 1          Bin 2\n');
printf('-----          -----\n');
for iC = 1:size(finalCombs, 1)
  str = num2str(finalCombs{iC, 1});
  printf('%s', str);
  printf('%s', char(32 * ones(1, 15 - length(str))));
  printf('%s\n', num2str(finalCombs{iC, 2}));
end

我合并了一个“NaN 球”,因为没有球被放入特定的容器中。通过使用 nchoosek 来增加球的数量,找到一组球的所有可能组合。为了每个组合存储不同数量的球,我需要使用元胞数组,这使得处理更加复杂,代码可能更难理解。

对于两个箱子和两个球,我们得到以下输出:

Possible combinations for bin 1 only:

Bin 1
-----
NaN
1
2
1  2


Possible combinations for bin 1 and bin 2:

Bin 1          Bin 2
-----          -----
NaN            NaN
NaN            1
NaN            2
NaN            1  2
1              NaN
1              2
2              NaN
2              1
1  2           NaN

如果我们使用三个球,我们得到:

Possible combinations for bin 1 only:

Bin 1
-----
NaN
1
2
3
1  2
1  3
2  3
1  2  3


Possible combinations for bin 1 and bin 2:

Bin 1          Bin 2
-----          -----
NaN            NaN
NaN            1
NaN            2
NaN            3
NaN            1  2
NaN            1  3
NaN            2  3
NaN            1  2  3
1              NaN
1              2
1              3
1              2  3
2              NaN
2              1
2              3
2              1  3
3              NaN
3              1
3              2
3              1  2
1  2           NaN
1  2           3
1  3           NaN
1  3           2
2  3           NaN
2  3           1
1  2  3        NaN

所以,现在,要将此代码概括为也支持任意数量的箱子,您必须将第二部分(不包括之前已经选择的球等)放入某个迭代过程,也许是递归过程。这可能需要大量工作,但所提出的方法应该是合适的。

希望对您有所帮助!

这是 Matlab 中的一个解决方案,完全 矢量化 。它应该很快。做法是:

  1. 创建一个矩阵,指示每个球进入哪个 bin(包括没有 bin 的可能性);
  2. 收集每个垃圾箱中的球。

第一部分只是 n+1 元素向量与自身 m 次的笛卡尔积。可以使用 dec2base, provided that n is less than 36. If not, it this approach 作为数字基础转换来完成。

第二部分使用 accumarray 高效完成。

m = 2; % data: number of balls
n = 3; % data: number of bins
t = (n+1)^m; % compute number of cases
[~, pos] = ismember(dec2base(0:t-1, n+1), ['1':'9' 'A':'Z']); % each column in this
% matrix refers to a ball, and tells which bin the ball goes to, or zero if no bin
[ii, jj, vv] = find(pos); % ii: combination; jj: ball: vv: bin
result = accumarray([ii vv], jj, [t n], @(x){sort(x).'}); % collect balls of each bin

例子

对于m=2, n=3

(抱歉使用图片)。

将m个球放入n个箱子,有n^m种组合。但是当你接受没有球到 m-1 球时,你的问题的答案是 m^0 + mC1 * n^1 + mC2 * n^2 + .... mCm * n^m

例如。将 3 个球放入 3 个箱子中,总数为 1 + 3*3 + 3*9 + 1*27 = 64

Python解决方案。

import itertools
#let bins be 'ABC', but u can have your own assignment
bins = 'ABC'
balls = '123'
tmp = []
for no_ball_used in range(len(balls)+1):
    bin_occupied = [''.join(list(i)) for i in itertools.product(bins,repeat=no_ball_used)]
    ball_used = [''.join(list(i)) for i in itertools.combinations(balls,no_ball_used)]
    solution = [i for i in itertools.product(ball_used,bin_occupied)]
    tmp.append(solution)

tmp 是完整列表,其中第一部分是使用的球,第二部分是使用的箱子。

print(tmp)
[[('', '')], [('1', 'A'), ('1', 'B'), ('1', 'C'), ('2', 'A'), ('2', 'B'), ('2', 'C'), ('3', 'A'), ('3', 'B'), ('3', 'C')], [('12', 'AA'), ('12', 'AB'), ('12', 'AC'), ('12', 'BA'), ('12', 'BB'), ('12', 'BC'), ('12', 'CA'), ('12', 'CB'), ('12', 'CC'), ('13', 'AA'), ('13', 'AB'), ('13', 'AC'), ('13', 'BA'), ('13', 'BB'), ('13', 'BC'), ('13', 'CA'), ('13', 'CB'), ('13', 'CC'), ('23', 'AA'), ('23', 'AB'), ('23', 'AC'), ('23', 'BA'), ('23', 'BB'), ('23', 'BC'), ('23', 'CA'), ('23', 'CB'), ('23', 'CC')], [('123', 'AAA'), ('123', 'AAB'), ('123', 'AAC'), ('123', 'ABA'), ('123', 'ABB'), ('123', 'ABC'), ('123', 'ACA'), ('123', 'ACB'), ('123', 'ACC'), ('123', 'BAA'), ('123', 'BAB'), ('123', 'BAC'), ('123', 'BBA'), ('123', 'BBB'), ('123', 'BBC'), ('123', 'BCA'), ('123', 'BCB'), ('123', 'BCC'), ('123', 'CAA'), ('123', 'CAB'), ('123', 'CAC'), ('123', 'CBA'), ('123', 'CBB'), ('123', 'CBC'), ('123', 'CCA'), ('123', 'CCB'), ('123', 'CCC')]]

tmp[0] = 0 个球的组合

tmp[1] = 1 个球的组合

tmp[2] = 2 个球的组合

tmp[3] = 3 个球的组合

您可以使用

解压 tmp
[item for sublist in tmp for item in sublist]