我有一些数字,让我们说
[X1,Y1], [X2,Y2] ..... [Xn,Yn] Y > X
我有另一个号码,比如Z.现在我想要的是将Z分成随机值Nm,使其始终满足以下条件:
N1 + N2 + N3 ...... Nn = Z
Nm > 0
Nm <= Ym - Xm
答案 0 :(得分:0)
IF the series y_1 - x_1 + … + y_m - x_m < z OR x_i ≥ y_i for any i
FAIL
ELSE DO
residual := z
FOR i := 1 to m-1
n_i := (y_i - x_i) × random (0,1]
residual := residual - n_i
IF residual ≤ 0
BREAK
n_m := residual
WHILE n_m ≤ 0 OR n_m > y_m-x_m
n_m之前的每个n_i都落入范围内,因为它是0到1之间的随机数的上限。最后一个值属于范围,因为如果没有,我们将循环并再次尝试。数字加起来为z,因为n_m是z与系列其余部分之和的差值。
答案 1 :(得分:0)
你有M个区间[Xi,Yi],你想在每个区间有一个随机数,这样所有数字的总和就是Z.
这些数字的总和必须在SUM(Xi)和SUM(Yi)之间。所以我们计算两者:
Zmin = 0;
Zmax = 0;
for (i = 0; i < m; i++) {
Zmin += X[i];
Zmax += Y[i];
Z[i] = X[i];
}
现在我们检查Z是否在Zmin和Zmax之间。如果不是,则无法获得满足约束条件的Z向量。
如果Z介于Zmin和Zmax之间,我们可以设想从Z向量开始,它是X的副本,并在所有Zi中随机地将Z和Zmin之间的差异洒在上面。
remainder = Z - Zmin;
if ((remainder < 0) || (remainder + Zmin > Zmax)) {
// Can't do.
}
// There are several ways of equidistributing the remainder.
// This is not one of them, but it's simple.
// If Z is very near Zmax, performances will suffer.
while (remainder > 0) {
// Choose
i = rand()*m;
// Can we increase Z[i]?
if (Z[i] < Y[i]) {
Z[i]++;
remainder--;
}
}
// DONE!
在O(n)中运行的更复杂的方法是将m个槽中的余数R与每个槽的宽度成比例分配。平均而言,槽(Y [i] -X [i])宽应该接收额外的R *(Y [i] -X [i])/(Zmax-Zmin)。我们可以使用Bresenham的算法尽可能地做到这一点,但如果我们想要一个随机分布,我们就冒着分配前K个槽的风险,并且有一个很大的余数,即使我们最大化了所有的剩下的插槽我们仍然不会用尽它。
为了避免这种情况,我们需要保持一个运行总数,我们可以推迟到以下间隔,从Zmax - Zmin开始。因此,在迭代i中,如果从i + 1到m的下一个间隔可以调度到D,并且余数是R,那么我们添加到Z [i]的任何东西都必须离开(余数 - Z [i] + X [i]&lt; = D),这意味着Z [i] -X [i]> =余数-D;即,我们添加到Z [i]的随机数(当前仍然等于X [i])必须从MAX开始(余数-D,0)。理想情况下,这将始终为0.因此,我们可以将MAX(余数-D,0)中的随机数归因于Y [i] -X [i]。设这个间隔宽度为Q.我们希望平均分配Q*remainder/(D+Q)
到这个间隔,这意味着Z[i] = Y[i]-X[i]-Q+rand()*(Q*remainder/(D+Q))
。
D = Zmax - Zmin;
for (i = 0; i < m; i++) {
V = MAX(remainder-D, 0);
Q = (Y[i] - X[i]) - V; // This is positive, or we would have failed earlier
Z[i] = Y[i]-X[i]-Q+rand()*(Q*remainder/(D+Q));
remainder -= (Z[i] - X[i]);
}
这是PHP中的快速实现,似乎有效。
<?php
$X = [ 1, 4, 11, 3, 5, 17, 22, 35, 120, 0 ];
$Y = [ 8, 9, 33, 9, 9, 28, 24, 36, 215, 3 ];
$m = count($X);
$ZZ= 0;
// Construct a Z that will work.
for ($i = 0; $i < $m; $i++) {
// $Y[$i] = rand($X[$i], $X[$i] + 20);
$ZZ += rand($X[$i], $Y[$i]);
}
$Zmin = 0;
$Zmax = 0;
for ($i = 0; $i < $m; $i++) {
$Zmin += $X[$i];
$Zmax += $Y[$i];
}
$R = $ZZ - $Zmin;
if (($R < 0) || ($R + $Zmin > $Zmax)) {
die("Can't do.\n");
}
$D = $Zmax - $Zmin;
for ($i = 0; $i < $m; $i++) {
$A = max($R-$D, 0); // Cannot distribute less than this.
$B = min($Y[$i]-$X[$i], $R); // Nor more than this.
$Q = ($B - $A); // This is positive, or we would have failed earlier
assert('$Q > 0');
$Z[$i] = $X[$i] + rand($A, $B); // floor($A + ($B-$A)*rand());
$R -= ($Z[$i] - $X[$i]);
assert('$R >= 0');
assert('$X[$i] <= $Z[$i]');
assert('$Y[$i] >= $Z[$i]');
}