【问题标题】:Python: get all the possible combinations for allocating x apples to y baskets subject to constraintPython:获取所有可能的组合,用于将 x 个苹果分配给 y 个受约束的篮子
【发布时间】:2019-03-07 01:41:46
【问题描述】:

假设我们有xapples 和y 篮子,我们希望将所有苹果分配到篮子中,这样每个篮子最多得到z 苹果。如何编写 Python 代码以获得所有可能的组合。 对于少数y,我可以对y进行如下循环(x=5,y=3,z=2)。

all_chances = np.zeros((0,3))
for a in range(3):
   for b in range(3):
      for c in range(3):
          if a+b+c == 5:
             all_chances = np.vstack((all_chances, np.array([a,b,c])))

基本上,all_chances

array([[1., 2., 2.],
   [2., 1., 2.],
   [2., 2., 1.]])

我的问题是:如果 y 是一个很大的数,比如 x = 30、y = 26、z=2,该怎么办?我需要循环 26 次吗?

【问题讨论】:

  • 是的,考虑到生成组合的算法,您必须嵌套 26 个循环,每个篮子一个。显然,这种方法并不是特别可行。相反,请尝试设置 y 篮子数组。循环遍历篮子数组,而不是每个篮子嵌套一个循环。
  • 两个建议:(1)使用某种形式的递归(2)使用Young图

标签: python numpy combinations permutation


【解决方案1】:

我弄乱了你的问题...尝试实施一种基于树的方法,因为我认为这很聪明,但我的笔记本电脑卡住了。我很好奇无论如何我们要对这些大数字寻找多少排列,并将问题(对我自己)改变为简单地计算排列,看看它是否甚至可以在轻型笔记本电脑上实现。

我得到了 154,135,675,070 个独特的排列。

开始...我搞砸了 itertools,并且排列永远需要长度为 26 的列表。所以...为了提醒自己至少计算不同排列的长期被遗忘的公式,我发现了这个.. .https://socratic.org/questions/how-many-distinct-permutations-can-be-made-from-the-letters-of-the-word-infinity

我运行了以下内容来计算。它在一秒钟内运行。

from numpy import prod
from math import factorial
import itertools

# number of unique permutations
def count_distinct_permutations(tup):
    value_counts = [len(list(grp)) for _, grp in itertools.groupby(tup)]
    return factorial(sum(value_counts)) / prod([float(factorial(x)) for x in value_counts])

# starting values
x = 30 # apples
y = 26 # baskets
z = 3  # max per basket

# count possible results
result = 0
for combos in itertools.combinations_with_replacement(range(z), y):
    if sum(combos) == x:
        result += count_distinct_permutations(combos)

现在...这显然不能回答您的具体问题。老实说,无论如何我都无法在内存中保存您正在寻找的结果。但是...您可以对此做出一些推论...使用您选择的值,只有 12 种值组合,但每个组合的排列在 15k 到 5000 万之间。

您可以查看每个组合...在 count_distinct_permutations() 函数中,itertools.groupby 会为您提供来自 (0,1,2) 的每个数字中有多少在组合中,您可以使用这十二个结果中的每一个来推断一些东西。不确定是什么,但我也不太确定如何处理 1540 亿个长度为 26 的列表。:)

希望这里有一些有用的东西,即使它没有回答您的确切问题。祝你好运!

【讨论】:

  • 我得到相同数量的154,135,675,070 分区使用我认为完全不同的方法。所以这让人放心..
【解决方案2】:

这是一种基于杨氏图的方法。例如,4 个篮子,6 个鸡蛋,每个篮子最多 3 个鸡蛋。如果我们按照篮子的装满程度来排序,我们会得到杨氏图。

x x x x   x x x x   x x x     x x x      x x
x x       x         x x x     x x        x x
          x                   x          x x

下面的代码列举了所有可能的 Young 图,并列举了所有可能的排列。

同样的逻辑也可以用来计数。

from itertools import product, combinations
from functools import lru_cache
import numpy as np

def enum_ord_part(h, w, n, o=0):
    if h == 1:
        d = n
        for idx in combinations(range(w), d):
            idx = np.array(idx, int)
            out = np.full(w, o)
            out[idx] = o+1
            yield out
    else:
        for d in range((n-1)//h+1, min(w, n) + 1):
            for idx, higher in product(combinations(range(w), d),
                                       enum_ord_part(h-1, d, n-d, o+1)):
                idx = np.array(idx)
                out = np.full(w, o)
                out[idx] = higher
                yield out

def bc(n, k):
    if 2*k > n:
        k = n-k
    return np.prod(np.arange(n-k+1, n+1, dtype='O')) // np.prod(np.arange(1, k+1, dtype='O'))

@lru_cache(None)
def count_ord_part(h, w, n):
    if h == 1:
        return bc(w, n)
    else:
        return sum(bc(w, d) * count_ord_part(h-1, d, n-d)
                   for d in range((n-1)//h+1, min(w, n) + 1))

几个例子:

>>> for i, l in enumerate(enum_ord_part(3, 4, 6), 1):
...     print(l, end=' ' if i % 8 else '\n')
... 
[3 3 0 0] [3 0 3 0] [3 0 0 3] [0 3 3 0] [0 3 0 3] [0 0 3 3] [3 2 1 0] [2 3 1 0]
[3 1 2 0] [2 1 3 0] [1 3 2 0] [1 2 3 0] [2 2 2 0] [3 2 0 1] [2 3 0 1] [3 1 0 2]
[2 1 0 3] [1 3 0 2] [1 2 0 3] [2 2 0 2] [3 0 2 1] [2 0 3 1] [3 0 1 2] [2 0 1 3]
[1 0 3 2] [1 0 2 3] [2 0 2 2] [0 3 2 1] [0 2 3 1] [0 3 1 2] [0 2 1 3] [0 1 3 2]
[0 1 2 3] [0 2 2 2] [3 1 1 1] [1 3 1 1] [1 1 3 1] [1 1 1 3] [2 2 1 1] [2 1 2 1]
[2 1 1 2] [1 2 2 1] [1 2 1 2] [1 1 2 2]
>>> 
>>> print(f'{count_ord_part(2, 26, 30):,}')
154,135,675,070
>>> print(f'{count_ord_part(50, 30, 1000):,}')
63,731,848,167,716,295,344,627,252,024,129,873,636,437,590,711

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 2021-06-05
    • 2020-12-30
    相关资源
    最近更新 更多