考虑以下整数范围为5-25的玩具数组:

a = np.array([12, 18, 21])

如何生成5随机整数数组,范围从1-5
它等于数组中的每个数字?该解决方案应在所有可能的输出上产生均匀分布。
到目前为止,我已经创建了一个简单的函数,它将产生a随机
整数:
import numpy as np

def foo(a, b):
    p = np.ones(b) / b
    return np.random.multinomial(a, p, size = 1)

使用数组5*中的值的示例:
In [1]: foo(12, 5)
Out[1]: array([[1, 4, 3, 2, 2]])

In [2]: foo(18, 5)
Out[2]: array([[2, 2, 3, 3, 8]])

In [3]: foo(21, 5)
Out[3]: array([[6, 5, 3, 4, 3]])

显然,这些整数具有所需的总和,但它们并不总是有界的
介于a1之间。
预期产出大致如下:
In [4]: foo(np.array([12, 18, 21]), 5)
Out[4]:
array([[1, 4, 3, 2, 2],
       [4, 3, 3, 3, 5],
       [5, 5, 4, 4, 3]])

*函数只接受整数作为参数。

最佳答案

这里有一个精确的(每个合法的和都有相同的概率)解它使用所有合法和的枚举,并不是指我们遍历每一个和,而是给定一个数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

演示:
# 4 values between 1 and 5 summing to 12
# a million samples takes a few seconds
x = random_capped_partition(1,5,4,12,1000000)

# sanity checks

# values between 1 and 5
x.min(),x.max()
# (1, 5)

# all legal sums occur
# count them brute force
sum(1 for a in range(1,6) for b in range(1,6) for c in range(1,6) if 7 <=     a+b+c <= 11)
# 85
# and count unique samples
len(np.unique(x,axis=0))
# 85

# check for uniformity
np.unique(x, axis=0, return_counts=True)[1]
# array([11884, 11858, 11659, 11544, 11776, 11625, 11813, 11784, 11733,
#        11699, 11820, 11802, 11844, 11807, 11928, 11641, 11701, 12084,
#        11691, 11793, 11857, 11608, 11895, 11839, 11708, 11785, 11764,
#        11736, 11670, 11804, 11821, 11818, 11798, 11587, 11837, 11759,
#        11707, 11759, 11761, 11755, 11663, 11747, 11729, 11758, 11699,
#        11672, 11630, 11789, 11646, 11850, 11670, 11607, 11860, 11772,
#        11716, 11995, 11802, 11865, 11855, 11622, 11679, 11757, 11831,
#        11737, 11629, 11714, 11874, 11793, 11907, 11887, 11568, 11741,
#        11932, 11639, 11716, 12070, 11746, 11787, 11672, 11643, 11798,
#        11709, 11866, 11851, 11753])

简要说明:
我们使用一个简单的递归来计算封顶分区的总数。我们在第一个箱子上进行拆分,即固定第一个箱子中的编号,然后通过重复检索填充其余箱子的方法的编号。然后我们简单地对不同的第一个bin选项求和。我们使用缓存装饰器来控制递归这个decorator会记住我们已经完成的所有参数组合,因此当我们通过不同的递归路径得到相同的参数时,就不必再进行相同的计算。
枚举的工作原理类似假设词典顺序。如何找到第n个分区再次,在第一个箱子上分开因为我们知道对于每个值,第一个bin可以采用剩余的bin可以填充的方式总数,所以我们可以形成累加和,然后看看n适合哪里如果n位于i和i+1部分和之间,则第一个索引是i+low,我们从n减去i和,然后重新开始剩余的容器。

关于python - 生成已知总数的随机整数数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58353929/

10-14 19:03
查看更多