Python "Stars and Bars"
Python "Stars and Bars"
我正在尝试获得所有可能的方法来将 n 颗糖果分配给 k children。
例如,根据 stars-and-bars 公式,将 96 颗糖果分配给 5 children 的方法数是 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 生成此列表的速度如此之慢?我错过了什么?
我发现你的数学有两个问题。
首先,您在那里描述了一个组合。实际上,你在想(96 选择 5),这并没有涵盖所有排列。
其次,排列实际上是 96!/91!,比 ~400 万several orders of magnitude higher。
只需添加字节数,you're in the high gigabyte range 现在的内存使用量, 可以 解释为什么您的机器变慢了;仅由此产生的内存使用量就足以压垮大多数现代消费类机器。
这是使您的方法奏效的一种方法。它使用 itertools.combinations
。构建完整列表需要几秒钟。要获得更快的基于 numpy
的方法,请参阅此 post.
的底部
它的工作原理是枚举 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
:
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]])
这大约需要一秒钟。
我正在尝试获得所有可能的方法来将 n 颗糖果分配给 k children。
例如,根据 stars-and-bars 公式,将 96 颗糖果分配给 5 children 的方法数是 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 生成此列表的速度如此之慢?我错过了什么?
我发现你的数学有两个问题。
首先,您在那里描述了一个组合。实际上,你在想(96 选择 5),这并没有涵盖所有排列。
其次,排列实际上是 96!/91!,比 ~400 万several orders of magnitude higher。
只需添加字节数,you're in the high gigabyte range 现在的内存使用量, 可以 解释为什么您的机器变慢了;仅由此产生的内存使用量就足以压垮大多数现代消费类机器。
这是使您的方法奏效的一种方法。它使用 itertools.combinations
。构建完整列表需要几秒钟。要获得更快的基于 numpy
的方法,请参阅此 post.
它的工作原理是枚举 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
:
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]])
这大约需要一秒钟。