这是一个精确的(每个合法的和都有相同的概率)解。它使用所有合法和的枚举,并不是说我们要遍历每个和,而是给定一个数字n,我们可以直接计算枚举中的第n个和。由于我们也知道合法和的总数,我们可以简单地画出统一的整数并对其进行转换:import numpy as np
import functools as ft
#partition count
@ft.lru_cache(None)
def capped_pc(N,k,m):
if N < 0:
return 0
if k == 0:
return int(N<=m)
return sum(capped_pc(N-i,k-1,m) for i in range(m+1))
capped_pc_v = np.vectorize(capped_pc)
def random_capped_partition(low,high,n,total,size=1):
total = total - n*low
high = high - low
if total > n*high or total < 0:
raise ValueError
idx = np.random.randint(0,capped_pc(total,n-1,high),size)
total = np.broadcast_to(total,(size,1))
out = np.empty((size,n),int)
for j in range(n-1):
freqs = capped_pc_v(total-np.arange(high+1),n-2-j,high)
freqs_ps = np.c_[np.zeros(size,int),freqs.cumsum(axis=1)]
out[:,j] = [f.searchsorted(i,"right") for f,i in zip(freqs_ps[:,1:],idx)]
idx = idx - np.take_along_axis(freqs_ps,out[:,j,None],1).ravel()
total = total - out[:,j,None]
out[:,-1] = total.ravel()
return out + low
演示:
^{pr2}$
简要说明:
我们使用一个简单的递归来计算封顶分区的总数。我们在第一个箱子上分开,也就是说,我们固定第一个箱子里的数量,然后通过重复的方法来检索填充剩余箱子的方法的数量。然后我们简单地总结不同的第一个箱子选项。我们使用缓存装饰器来控制递归。这个decorator会记住我们已经完成的所有参数组合,所以当我们通过不同的递归路径得到相同的参数时,我们就不必再做同样的计算了。在
枚举的工作原理类似。假设字典顺序。如何找到第n个分区?再次,在第一个箱子上分开。因为我们知道对于每个值,第一个bin可以采用填充剩余bin的方法的总数,所以我们可以形成累积和,然后看看n在哪里适合。如果n位于第i个和第i+1个部分和之间,则第一个索引为i+low,我们从n中减去第i个和,然后重新开始剩余的bin。在