Python“星条”

时间:2018-11-30 16:56:50

标签: python combinatorics

我正在尝试所有可能的方式在k个孩子中分配n个糖果。 例如,根据星条图公式,在5个孩子中分配96个糖果的方式是100! / (96!*4!) = 3 921 225个元组,其所有可能排列的大小为5。

list2 = [item for item in it.product(range(97), repeat = 5)
             if sum(item) == 96]

我的电脑似乎不知所措。 每个元组消耗24 * 5 = 120字节的内存。这导致921 225 * 120 = 470547000字节或450 mb。好像没那么多。为什么PC生成此列表这么慢?我想念什么?

2 个答案:

答案 0 :(得分:1)

我发现您的数学有两个问题。

首先,您要在此处描述组合。实际上,您在考虑(96个选择5个),它不能涵盖所有排列。

第二,排列实际上是96!/ 91 !,比{400万。several orders of magnitude higher

只需添加字节数you're in the high gigabyte range现在的内存使用情况即可,可以解释为什么您的计算机速度变慢;仅由此产生的内存使用量就可能使大多数现代消费类计算机崩溃。

答案 1 :(得分:1)

这是使您的方法可行的一种方法。它使用itertools.combinations。构建完整列表需要花费几秒钟。有关基于numpy的更快方法,请参见本文底部。

通过枚举1到100之间的四个小节的所有组合,总是将外部小节0和101相加来工作。五个孩子的分配就是小节之间的差,即小节的差异减去一。 >

import numpy as np
import itertools


bars = [0, 0, 0, 0, 0, 101]
result = [[bars[j+1] - bars[j] - 1 for j in range(5)] for bars[1:-1] in itertools.combinations(range(1, 101), 4)]

# sanity check
len(result)
# 3921225
# show few samples
from pprint import pprint
pprint(result[::400000])
# [[0, 0, 0, 0, 96],
#  [2, 26, 12, 8, 48],
#  [5, 17, 22, 7, 45],
#  [8, 23, 30, 16, 19],
#  [12, 2, 73, 9, 0],
#  [16, 2, 25, 40, 13],
#  [20, 29, 24, 0, 23],
#  [26, 13, 34, 14, 9],
#  [33, 50, 4, 5, 4],
#  [45, 21, 26, 1, 3]]

为什么你的表现不好?我认为主要是因为您的循环有点浪费,97 ^ 5大于100选择4。

如果您真的想要快速,可以将itertools.combinations替换为numpy版本:

https://stackoverflow.com/a/42202157/7207392

def fast_comb(n, k):
    a = np.ones((k, n-k+1), dtype=int)
    a[0] = np.arange(n-k+1)
    for j in range(1, k):
        reps = (n-k+j) - a[j-1]
        a = np.repeat(a, reps, axis=1)
        ind = np.add.accumulate(reps)
        a[j, ind[:-1]] = 1-reps[1:]
        a[j, 0] = j
        a[j] = np.add.accumulate(a[j])
    return a

fb = fast_comb(100, 4)
sb = np.empty((6, fb.shape[1]), int)
sb[0], sb[1:5], sb[5] = -1, fb, 100
result = np.diff(sb.T) - 1

result.shape
# (3921225, 5)
result[::400000]
# array([[ 0,  0,  0,  0, 96],
#        [ 2, 26, 12,  8, 48],
#        [ 5, 17, 22,  7, 45],
#        [ 8, 23, 30, 16, 19],
#        [12,  2, 73,  9,  0],
#        [16,  2, 25, 40, 13],
#        [20, 29, 24,  0, 23],
#        [26, 13, 34, 14,  9],
#        [33, 50,  4,  5,  4],
#        [45, 21, 26,  1,  3]])

这大约需要一秒钟。