在非常特定的约束下生成随机数

时间:2011-10-26 20:52:50

标签: python algorithm random

我遇到了以下编程问题。我需要生成n (a, b)个元组,其中所有a的总和是给定的A,所有b的总和是给定的B并且对于每个元组,a / b的比率在(c_min, c_max)范围内。 A / B也在同一范围内。我也试图确保除了约束引入的结果之外没有偏差,a / b值在给定范围内或多或少均匀分布。

一些澄清和元约束:

    提供了
  • ABc_minc_max
  • 比率A / B位于(c_min, c_max)范围内。如果问题在于给出其他约束条件的解决方案,则必须如此。
  • a和b为>0且非整数。

我正在尝试用Python实现这一点,但我们非常感谢任何语言(包括英语)的想法。

6 个答案:

答案 0 :(得分:3)

我们寻找元组a_i和b_i,以便

  • (a_1,... a_n)和(b_1,... b_n)的分布在索引的排列下是不变的(你称之为“无偏见的”)
  • 比率a_i / b_i均匀分布在[cmin,cmax]
  • sum(a_i)= A,sum(b_i)= B

如果c_minc_max条件不太严重(即它们不是非常接近另一个),并且n不是很大,则以下情况有效:

  • 统一生成a_isum a_i = A
    • 从某个发行版中抽取n个样本aa_ii = 1..n)(例如,制服)
    • 将它们除以它们的总和并乘以A:a_i = A * aa_i / sum(aa_i)具有所需的属性。
  • 使用相同的方法生成b_i sum b_i = B
  • 如果i存在a_i / b_i[cmin, cmax]不在a_i区间内,请将所有 b_i和{{1}并从头开始重试。

n一致,因为满足约束的a_ib_i集合变得越来越窄{{1增加(因此你拒绝更多候选人)。

老实说,我没有看到任何其他简单的解决方案。如果n变大且n,则必须使用大锤(例如MCMC)从您的发行版生成样本,除非有一些我们没有看到的技巧。


如果您确实想要使用MCMC算法,请注意您可以将cmin ~ cmax更改为cmin(同样适用于cmin * B / A)并假设cmax。那么问题是在两个单位n-simplices(u_1 ... u_n,v_1 ... v_n)的乘积上均匀绘制,以便

A == B == 1

所以你必须使用MCMC算法(Metropolis-Hastings似乎更适合)两个单位n-simplices的密度

的乘积
u_i / v_i \in [cmin, cmax].

绝对可行(虽然参与其中)。

答案 1 :(得分:2)

首先根据需要生成尽可能多的相同元组:

(A/n, B/n)

现在随机挑选两个元组。随机更改一个的a值,并对另一个的a值进行补偿更改,使所有内容保持在给定的约束内。把两个元组放回去。

现在选择另一对随机。这次会改变b值。

泡沫,冲洗重复。

答案 2 :(得分:2)

我认为最简单的事情是

  1. 使用您喜欢的方法抛出n-1的{​​{1}}值,并设置a_n以获得正确的总数。关于这样做有几个问题,虽然我从未见过我真的很开心的答案。也许我会写一篇论文或其他什么。

  2. 通过将\sum_i=0,n-1 a_i < A统一放在允许的范围内来获取n-1 b,并设置最终c_i以获得正确的总数并检查最后的c(我认为一定没问题,但我还没有证明)。

  3. 请注意,由于我们有2个硬约束,我们应该期望抛出b个随机数,而这个方法就是这样(假设您可以执行2n-2抛出的第1步。

答案 3 :(得分:1)

阻止吉布斯采样非常简单,并且收敛到正确的分布(这与亚历山大提出的方法一致)。

  1. 对于所有i,初始化 i = A / n和b i = B / n。
  2. 随机选择i≠j。以概率1/2,用满足约束的均匀随机值更新 i j 。其余的时间,对b i 和b j 执行相同的操作。
  3. 重复步骤2,因为您的应用程序似乎需要多次。我不知道收敛率是多少。

答案 4 :(得分:0)

所以这就是我从数学角度思考的问题。我们有序列a_ib_ia_i的总和为Ab_i的总和为B。此外A/B位于(x,y)中,a_i/b_i位于i。此外,您希望a_i/b_i均匀分布在(x,y)中。

从最后开始这样做。从c_i中选择(x,y),使其均匀分布。然后我们希望具有以下等级a_i/b_i = c_i,所以a_i = b_i*c_i

因此我们只需找到b_i。但我们有以下线性方程组:

A = (sum)b_i*c_i
B = (sum)b_i

其中b_i是变量。解决它(一些花哨的线性代数技巧),你就完成了!

请注意,对于足够大的n,此系统将提供大量解决方案。它们将取决于您可以随机选择的一些参数。


足够的理论方法,让我们看看一些实际的解决方案。

//编辑1:这是一些硬核Python代码:D

import random
min = 0.0
max = 10.0
A = 500.0
B = 100.0

def generate(n):
    C = [min + i*(max-min)/(n+1) for i in range(1, n+1)]
    Y = [0]
    for i in range(1,n-1):
        # This line should be changed in order to always get positive numbers
        # It should be relatively easy to figure out some good random generator
        Y.append(random.random())
    val = A - C[0]*B
    for i in range(1, n-1):
        val -= Y[i] * (C[i] - C[0])
    val /= (C[n-1] - C[0])
    Y.append(val)
    val = B
    for i in range(1, n):
        val -= Y[i]
    Y[0] = val
    result = []
    for i in range(0, n):
        result.append([ Y[i]*C[i], Y[i] ])
    return result

结果是满足条件的对(X,Y)列表,但它们可能是负数(参见代码中的随机生成器行),即第一对和最后一对可能包含负数。

//编辑2:

为了确保他们是积极的你可以尝试像

Y.append(random.random() * B / n)

而不是

Y.append(random.random())

我不确定。

//编辑3:

为了获得更好的效果,请尝试以下方法:

avrg = B / n
ran = avrg / 20
for i in range(1, n-1):
    Y.append(random.gauss(avrg, ran))

而不是

for i in range(1, n-1):
    Y.append(random.random())

这会使所有b_i都接近B / n。不幸的是,最后一个学期有时仍会高涨。对不起,但是因为上一个和第一个术语取决于其他术语,所以没有办法避免这个(数学)。对于小n(~100),它看起来不错。不幸的是,可能会出现一些负面价值。

如果您另外希望b_i均匀分布,那么选择正确的生成器并不是那么简单。

答案 5 :(得分:0)

这里有很多好主意。谢谢! Rossum 的想法似乎是最直接的实施方式,所以我去了。以下是后人的代码:

c_min = 0.25
c_max = 0.75
a_sum = 100.0
b_sum = 200.0
n = 1000 

a = [a_sum / n] * n
b = [b_sum / n] * n

while not good_enough(a, b):
    i, j = random.sample(range(n), 2)
    li, ui = c_min * b[i] - a[i], c_max * b[i] - a[i]
    lj, uj = a[j] - c_min * b[j], a[j] - c_max * b[j]
    llim = max((li, uj))
    ulim = min((ui, lj))
    q = random.uniform(llim, ulim)
    a[i] += q
    a[j] -= q

    i, j = random.sample(range(n), 2)
    li, ui = a[i] / c_max - b[i], a[i] / c_min - b[i]
    lj, uj = b[j] - a[j] / c_max, b[j] - a[j] / c_min
    llim = max((li, uj))
    ulim = min((ui, lj))
    q = random.uniform(llim, ulim)
    b[i] += q
    b[j] -= q

good_enough(a, b)功能可以是很多东西。我试过了:

  • 标准偏差,即击中或未命中,因为您不知道什么是足够好的值。
  • Kurtosis,其中较大的负值会很好。但是,它的计算速度相对较慢,并且未使用(a_sum / n, b_sum / n)的种子值进行定义(尽管这很难修复)。
  • 偏斜,需要接近0的值。但它与kurtosis具有相同的缺点。
  • n成比例的多次迭代。 2n有时是不够的,n ^ 2有点矫枉过正,而且是指数级的。

理想情况下,使用偏斜和峰度组合的启发式算法是最好的,但我决心确保每个值都从最初的值改变(再次,作为评论中建议的 rossum )。虽然没有理论上保证循环完成,但它对我来说似乎运作良好。