我遇到了以下编程问题。我需要生成n
(a, b)
个元组,其中所有a
的总和是给定的A
,所有b
的总和是给定的B
并且对于每个元组,a / b
的比率在(c_min, c_max)
范围内。 A / B
也在同一范围内。我也试图确保除了约束引入的结果之外没有偏差,a / b
值在给定范围内或多或少均匀分布。
一些澄清和元约束:
A
,B
,c_min
和c_max
。 A / B
位于(c_min, c_max)
范围内。如果问题在于给出其他约束条件的解决方案,则必须如此。>0
且非整数。我正在尝试用Python实现这一点,但我们非常感谢任何语言(包括英语)的想法。
答案 0 :(得分:3)
我们寻找元组a_i和b_i,以便
如果c_min
和c_max
条件不太严重(即它们不是非常接近另一个),并且n
不是很大,则以下情况有效:
a_i
“sum a_i = A
:
n
个样本aa_i
(i = 1..n
)(例如,制服)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_i
和b_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)
我认为最简单的事情是
使用您喜欢的方法抛出n-1
的{{1}}值,并设置a_n以获得正确的总数。关于这样做有几个问题,虽然我从未见过我真的很开心的答案。也许我会写一篇论文或其他什么。
通过将\sum_i=0,n-1 a_i < A
统一放在允许的范围内来获取n-1
b
,并设置最终c_i
以获得正确的总数并检查最后的c(我认为一定没问题,但我还没有证明)。
请注意,由于我们有2个硬约束,我们应该期望抛出b
个随机数,而这个方法就是这样(假设您可以执行2n-2
抛出的第1步。
答案 3 :(得分:1)
阻止吉布斯采样非常简单,并且收敛到正确的分布(这与亚历山大提出的方法一致)。
答案 4 :(得分:0)
所以这就是我从数学角度思考的问题。我们有序列a_i
和b_i
,a_i
的总和为A
,b_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)
功能可以是很多东西。我试过了:
(a_sum / n, b_sum / n)
的种子值进行定义(尽管这很难修复)。0
的值。但它与kurtosis具有相同的缺点。n
成比例的多次迭代。 2n
有时是不够的,n ^ 2
有点矫枉过正,而且是指数级的。理想情况下,使用偏斜和峰度组合的启发式算法是最好的,但我决心确保每个值都从最初的值改变(再次,作为评论中建议的 rossum )。虽然没有理论上保证循环完成,但它对我来说似乎运作良好。