我需要生成满足以下约束的 n 个随机实数值 P[0], P[1], ..., P[n-1]:
Pmin[0] <= P[0] <= Pmax[0]
Pmin[1] <= P[1] <= Pmax[1]
...
Pmin[n-1] <= P[n-1] <= Pmax[n-1]
P[0] + P[1] + ... + P[n-1] = S
知道如何有效地做到这一点吗?
我需要生成满足以下约束的 n 个随机实数值 P[0], P[1], ..., P[n-1]:
Pmin[0] <= P[0] <= Pmax[0]
Pmin[1] <= P[1] <= Pmax[1]
...
Pmin[n-1] <= P[n-1] <= Pmax[n-1]
P[0] + P[1] + ... + P[n-1] = S
知道如何有效地做到这一点吗?
一般来说,如果从给定的范围内随机均匀地选择元素,是不可能解决这个问题的。
示例 1:假设 Pmin[i] = 0 和 Pmax[i] = 1。假设 n = 10 和 S = 100。那么没有解,因为最大可能的和是 10。
示例 2:假设 Pmin[i] = 0 和 Pmax[i] = 1。假设 n = 10 和 S = 10。那么只有一个解决方案:选择 P[i] = 1。
可以编写一个算法,从可能的解决方案集中随机均匀地选择结果序列;这与说 P[i] 均匀分布在 Pmin[i] 和 Pmax[i] 之间完全不同。
基本思想是,在每个阶段,进一步限制你的范围,如下所示:
如果您在选择每个 P[i] 时能够遵守这些规则,那么就有一个解决方案,您会随机找到一个解决方案。否则,没有解决办法。
请注意,要实际随机选择解决方案,最好对索引进行洗牌,执行此算法,然后重新排列序列,使其顺序正确。您可以在 O(n) 中洗牌,执行此算法(此处推荐动态编程,因为您可以自下而上构建解决方案),然后通过“解洗”结果序列来吐出序列。
For every i, assign P[i] := Pmin[i]
Compute the sum
If sum>S, then stop (it's impossible)
For every i:
If P[i]+S-sum <= Pmax[i]
P[i] = P[i]+S-sum
Stop (it's done :-)
sum = sum+Pmax[i]-P[i]
P[i] = Pmax[i]
Go for next i
Stop (it's impossible)
哎呀,对不起,你说随机......这不是那么微不足道。让我想想...
运行前面的算法有一个起点。现在计算上下的总保证金。上面的边距是每个 i 的各个边距 Pmax[i]-P[i] 的总和。下面的边距是每个 i 的各个边距 P[i]-Pmin[i] 的总和。
以随机顺序遍历除一个之外的所有元素,每个元素只访问一次。对于他们每一个人:
然后调整剩余元素以适合总和 S。
关于随机顺序的遍历,请参阅Knuth shuffles。