【问题标题】:Python "Stars and Bars"Python“星星和酒吧”
【发布时间】:2019-05-02 20:40:59
【问题描述】:

我正在尝试寻找所有可能的方法来在 k 个孩子中分发 n 个糖果。 例如,根据星条公式,将 96 颗糖果分配给 5 个孩子的方法数是 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 生成此列表的速度如此之慢?我错过了什么?

【问题讨论】:

  • 数学对我来说不算数。 100 阶乘比 400 万大得多。
  • 首先,你打算用这个list2做什么?因为有更好的方法可以在不耗尽内存的情况下运行此操作,例如使用生成器或在循环中生成,您可以在此处阅读更多详细信息:stackoverflow.com/questions/231767/…
  • it.product(range(97), repeat = 5) 产生 97^5 = 8 587 340 257 个值。这需要一些时间。
  • @AdamDadvar 他的问题没有任何区别。
  • 看看my answer末尾的定宽分区码。

标签: python combinatorics


【解决方案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 大很多。

如果你想要它真的很快,你可以用numpy 版本替换itertools.combinations

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]])

这大约需要一秒钟。

【讨论】:

  • 如果我需要超过 4 个插槽的范围 (101) 的笛卡尔积,它会起作用吗?
【解决方案2】:

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

首先,您在那里描述了一个组合。实际上,您正在考虑(96 选择 5),这并未涵盖所有排列。

其次,排列实际上是 96!/91!,即 several orders of magnitude higher 比约 400 万。

只需添加字节数,you're in the high gigabyte range 现在的内存使用量,这可以解释您的机器速度变慢的原因;仅由此产生的内存使用量就可能会破坏大多数现代消费类机器。

【讨论】:

  • 这是假设没有缓存发生,我希望他们的it 模块正在处理。
  • @TheIncorrigible1:我相信在正常情况下,iterator 模块确实会生成记录,但由于列表理解在这里急切地引用了它,因此该缓存的任何好处都会充其量可能是最小的。
猜你喜欢
  • 1970-01-01
  • 2014-06-09
  • 2014-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-30
  • 1970-01-01
  • 2012-05-06
相关资源
最近更新 更多