生成带有条件的随机数列表-numpy

时间:2019-09-22 09:50:19

标签: pandas numpy random numpy-ndarray

我想生成一个由15个整数组成的列表,它们的总和为12,最小值为0,最大值为6。

我尝试了以下代码

def generate(low,high,total,entity):
   while sum(entity)!=total:
       entity=np.random.randint(low, high, size=15)
   return entity

但是上述功能无法正常工作。这太浪费时间了。 请让我知道生成此类数字的有效方法吗?

3 个答案:

答案 0 :(得分:4)

从严格意义上讲,以上内容将起作用。但是对于介于0和6之间的15个数字,生成12的几率并不高。实际上,我们可以使用以下方法计算可能性的数量:

对于0≤s≤6

F(s,1)= 1

F(s,n)=Σ 6 i = 0 F(s-i,n-1)

我们可以使用以下值进行计算:

from functools import lru_cache

@lru_cache()
def f(s, n, mn, mx):
    if n < 1:
        return 0
    if n == 1:
        return int(mn <= s <= mx)
    else:
        if s < mn:
            return 0
        return sum(f(s-i, n-1, mn, mx) for i in range(mn, mx+1))

这意味着在4'747'561'509'943个总可能性中,有9'483'280个可能性总和为12,即0.00019975%。因此,大约需要500'624次迭代才能得出这样的解决方案。

因此,我们应该更好地寻求找到一种简单的方法来生成这种序列。我们可以通过每次计算生成数字的概率来做到这一点:将 i 作为数字生成的概率作为 n 个数字序列中第一个数字的总和为< em> s 是 F(si,n-1,0,6)/ F(s,n,0,6)。这将确保我们在可能性列表上生成一个 uniform 列表,如果我们每次绘制一个统一数字,那么它将不会在与给定值匹配的整个值列表中与统一分布匹配条件:

我们可以递归地做到这一点:

from numpy import choice

def sumseq(n, s, mn, mx):
    if n > 1:
        den = f(s, n, mn, mx)
        val, = choice(
            range(mn, mx+1),
            1,
            p=[f(s-i, n-1, mn, mx)/den for i in range(mn, mx+1)]
        )
        yield val
        yield from sumseq(n-1, s-val, mn, mx)
    elif n > 0:
        yield s

使用上述功能,我们可以生成numpy数组:

>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 0, 0, 0, 4, 0, 3, 0, 1, 0, 0, 1, 2, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 1, 0, 0, 1, 4, 1, 0, 0, 2, 1, 0, 0, 2])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 1, 0, 0, 2, 0, 3, 1, 3, 0, 1, 0, 0, 0, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([5, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 0, 0, 4, 2, 3, 0, 0, 0, 0, 0, 3, 0, 0])

答案 1 :(得分:2)

您可以尝试以不同的方式实现它。

index.html

此外,如果您使用np.random.randint,您的最高分实际上将是高1

答案 2 :(得分:2)

嗯,有一个简单自然的解决方案-使用分配,根据定义,它为您提供具有固定总和的值数组。最简单的是Multinomial Distribution。添加的唯一代码是,如果某些采样值大于最大值,则检查并拒绝(并重复采样)。

沿线

import numpy as np

def sample_sum_interval(n, p, maxv):
    while True:
        q = np.random.multinomial(n, p)
        v = np.where(q > maxv)
        if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
            return q
    return None

np.random.seed(32345)

k    = 15
n    = 12
maxv = 6
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, p, maxv)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, p, maxv)
print(q)
print(np.sum(q))

更新

我迅速查看了@WillemVanOnsem提出的方法,我相信它与我自己使用的多项式不同。

如果我们查看多项式PMF,并假设所有k个数字的概率相等, p 1 = ... = p k = 1 / k,那么我们可以将PMF编写为

PMF(x 1 ,... x k )= n!/(x 1 !... x k !)p 1 x 1 ... p k x k = n!/(x 1 !... x k !)k -x 1 ... k -x k = n!/(x 1 !... x k !)k -Sum i x i = n!/(x 1 !... x k !)k -n

很明显,由于分母的阶乘(当然是模排列),特定的x 1 ... x k 组合的概率会彼此不同。我认为@WillemVanOnsem方法与@WillemVanOnsem方法不同,后者的所有人都有相同的出现概率。

故事的道德-这些方法产生不同的分布。