<p>下面是一个递归解决方案,它通过将适当数量的项分配给第一个代理,然后将问题的其余部分交给进一步的自身调用来实现:</p>
<pre><code>from itertools import combinations
def part(agents, items):
if len(agents) == 1:
yield {agents[0]: items}
else:
quota = len(items) // len(agents)
for indexes in combinations(range(len(items)), quota):
remainder = items[:]
selection = [remainder.pop(i) for i in reversed(indexes)][::-1]
for result in part(agents[1:], remainder):
result[agents[0]] = selection
yield result
</code></pre>
<p>在单个代理的简单情况下,我们生成一个字典并终止。在</p>
<p>如果有多个代理,我们:</p>
<ol>
<li><p>将应分配给第一个代理的索引的所有组合生成到<code>items</code>中。</p></li>
<li><p>将这些索引处的项按相反顺序从<code>items</code>的副本弹出到<code>selection</code>,然后用<code>[::-1]</code>将结果反转回来,以保持预期的顺序。</p></li>
<li><p>对其余的代理和项递归调用<code>part()</code>。</p></li>
<li><p>将我们已经做出的选择添加到这些递归调用生成的每个结果中,并生成该结果。</p></li>
</ol>
<p>这就是它的作用:</p>
^{pr2}$
<p/>
<pre><code>>>> for p in part(["A", "B", "C"], [1, 2, 3, 4, 5, 6, 7, 8, 9]):
... print(p)
...
{'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 7], 'C': [6, 8, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 8], 'C': [6, 7, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 9], 'C': [6, 7, 8]}
{'A': [1, 2, 3], 'B': [4, 6, 7], 'C': [5, 8, 9]}
# [...]
{'A': [7, 8, 9], 'B': [3, 4, 5], 'C': [1, 2, 6]}
{'A': [7, 8, 9], 'B': [3, 4, 6], 'C': [1, 2, 5]}
{'A': [7, 8, 9], 'B': [3, 5, 6], 'C': [1, 2, 4]}
{'A': [7, 8, 9], 'B': [4, 5, 6], 'C': [1, 2, 3]}
</code></pre>
<p/>
<pre><code>>>> for p in part(["A", "B", "C"], [1, 2, 3, 4, 5, 6, 7]):
... print(p)
...
{'A': [1, 2], 'B': [3, 4], 'C': [5, 6, 7]}
{'A': [1, 2], 'B': [3, 5], 'C': [4, 6, 7]}
{'A': [1, 2], 'B': [3, 6], 'C': [4, 5, 7]}
{'A': [1, 2], 'B': [3, 7], 'C': [4, 5, 6]}
# [...]
{'A': [6, 7], 'B': [2, 5], 'C': [1, 3, 4]}
{'A': [6, 7], 'B': [3, 4], 'C': [1, 2, 5]}
{'A': [6, 7], 'B': [3, 5], 'C': [1, 2, 4]}
{'A': [6, 7], 'B': [4, 5], 'C': [1, 2, 3]}
</code></pre>
<p>如您所见,它处理<code>items</code>不能在<code>agents</code>之间等分的情况。此外,与各种基于<code>permutations()</code>的答案不同,它不会浪费计算重复结果的工作量,因此运行速度比它们快得多。在</p>