如果我正确理解规范,您希望随机生成受限整数compositions,这样每个可能的组合都有相同的被选择的可能性。在
我们可以将this answer应用于均匀生成随机整数partition的问题,从而精确地解决小输入值问题。我们只需要一种方法来计算受限制的k-组成。在数学的this answer中,有一个递归公式可以用来解决一个相关的问题,但事实证明,作为this answer的一部分,有一个更明确的公式,它使用二项式系数。下面是一个纯Python实现:import functools
import random
@functools.lru_cache(1 << 10)
def C1(n, k, a, b):
"Counts the compositions of `n` into `k` parts bounded by `a` and `b`"
return C2(n - k*(a - 1), k, b - (a - 1))
def C2(n, k, b):
"Computes C(n, k, 1, b) using binomial coefficients"
total = 0
sign = +1
for i in range(0, k + 1):
total += sign * choose(k, i) * choose(n - i*b - 1, k - 1)
sign = -sign
return total
def choose(n, k):
"Computes the binomial coefficient of (n, k)"
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
k = min(k, n - k)
c = 1
for i in range(k):
c = c * (n - i) // (i + 1)
return c
def check_pre_and_post_conditions(f):
def wrapper(n, k, a, b):
assert 1 <= k <= n, (n, k)
assert 1 <= a <= b <= n, (n, a, b)
assert k*a <= n <= k*b, (n, k, a, b)
comp = f(n, k, a, b)
assert len(comp) == k, (len(comp), k, comp)
assert sum(comp) == n, (sum(comp), n, comp)
assert all(a <= x <= b for x in comp), (a, b, comp)
return comp
return functools.wraps(f)(wrapper)
@check_pre_and_post_conditions
def random_restricted_composition(n, k, a, b):
"Produces a random composition of `n` into `k` parts bounded by `a` and `b`"
total = C1(n, k, a, b)
which = random.randrange(total)
comp = []
while k:
for x in range(a, min(b, n) + 1):
count = C1(n - x, k - 1, a, b)
if count > which:
break
which -= count
comp.append(x)
n -= x
k -= 1
return comp
要选择一个随机组合,我们只需生成一个比可能的组合总数小的随机索引,然后构造i-th字典组合(有关使用的递归关系的解释,请参阅相关问题)。这应该以同样的概率产生所有可能的结果。在
但是,因为C1(n, k, a, b)是指数增长的,对于n和{}的大值,这种方法相当慢。对于较大的值,近似的解决方案会更好地为您服务。在