我已经知道如何在一个范围内生成随机数。我可以通过使用做到这一点
rand.nextInt((max - min) + 1) + min;
Run Code Online (Sandbox Code Playgroud)
问题是我还想为这些数字设置一个标准偏差。数字也必须是正数,并且不在 0 和 1 之间
编辑我删除了 ThreadLocalRandom 类,因为我无法在该类中设置种子,并且这些随机数应该可以在不同的系统中重现。
为有界分布选择标准偏差(或方差)只能在取决于所选分布和(min, max)区间边界的约束条件下完成。某些分布可能允许您将方差设置为任意小(例如Beta 分布),而其他分布(例如均匀分布)一旦(min, max)设置了界限就不允许有任何灵活性。在任何情况下,您都无法将方差设置为任意大 - 边界确实会阻止这种情况(它们总是会输入分布方差的表达式)。
我将通过一个非常简单的示例来说明这一点,该示例无需任何 3rd 方库即可实现。假设您想要区间上的对称分布(min, max),对称意味着分布的均值 E(X) 位于区间的中间:E(X) = (min + max)/2。
使用 Random's nextDoubleas inx = a + (b - a) * rnd.nextDouble()将为您提供a <= x < b具有固定方差的区间中的均匀分布的随机变量Var(X) = (b - a)^2 / 12(不是我们想要的)。
OTH,在相同间隔上模拟对称三角形分布(a, b)会给我们一个随机变量,具有相同的平均值但只有一半的方差:(Var(X) = (b - a)^2 / 24也是固定的,所以也不是我们想要的)。
带参数的对称梯形分布(a < b < c < d)位于均匀分布和三角形分布中间的某处(a, d)。对称条件意味着d - c = b - a,在下文中,我将距离b - a称为x或称为“位移”(我已经编造了这个名字,它不是一个技术术语)。
如果x从上方接近 0.0,梯形将开始看起来非常类似于均匀分布,其方差将趋向于最大可能值(d - a)^2 / 12。如果从下方x接近最大可能值(d - a)/2,梯形将看起来非常类似于对称三角形分布,其方差将接近 的最小可能值(d - a)^2 / 24)(但请注意,我们应该远离这些极端值,以免打破方差公式或我们的梯形算法)。
因此,我们的想法是构建一个梯形分布,其值x可以产生您想要的标准偏差,前提是您的目标标准偏差必须位于由 给出的开放范围(大致)内(0.2041(d - a), 0.2886(d - a))。为方便起见,我们假设这a = min = 2.0和d = max = 10.0这给了我们这个范围可能stddevs的:(1.6328, 2.3088)。让我们进一步假设我们想要构建一个标准差为 的分布2.0(当然,它必须在可接受的范围内)。
解决这个问题需要3个步骤:
1)我们需要有一个给定方差的公式min, max和一个可接受的位移值x
2)我们需要以某种方式“反转”这个表达式来x为我们提供目标方差的值
3) 一旦我们知道了 的值,x我们必须构造一个随机变量,它的参数为对称梯形分布(min, max, x)
第 1 步:
/**
* Variance of a symmetric trapezoidal distribution with parameters
* {@code a < b < c < d} and the length of {@code d - c = b - a}
* (by symmetry) identified by {@code x}.
*
* @param a support lower bound
* @param d support upper bound
* @param x length of {@code d - c = b - a}, constrained to lie in the open
* interval {@code (0, (d-a)/2)}
* @return variance of the symmetric trapezoidal distribution defined by
* the triple {@code (a, d, x)}
*/
static double varSymTrapezoid(double a, double d, double x) {
if (a <= 0.0 || d <= 0.0 || a >= d) {
throw new IllegalArgumentException();
}
if (x <= 0.0 || x >= (d - a) / 2) {
throw new IllegalArgumentException();
}
double b = a + x;
double c = d - x;
double b3 = pow(b, 3);
double c3 = pow(c, 3);
double ex2p1 = pow(b, 4) / 4 - a * b3 / 3 + pow(a, 4) / 12;
double ex2p2 = (c3 / 3 - b3 / 3) * (d - c);
double ex2p3 = pow(c, 4) / 4 - d * c3 / 3 + pow(d, 4) / 12;
double ex2 = (ex2p1 + ex2p2 + ex2p3) / ((d - b) * (d - c));
return ex2 - pow((a + d) / 2, 2);
}
Run Code Online (Sandbox Code Playgroud)
请注意,此公式仅适用于对称梯形分布。举个例子,如果你用 2.5 ( varSymTrapezoid(2.0, 10.0, 2.5))的位移调用这个方法,它会给你一个3.0416太低的方差(我们需要 4.0),这意味着 2.5 的位移太大了(更高的位移给出更低的差异)。
方差表达式是一个四阶多项式,x因为我不想解析求解。然而,对于x可接受范围内的目标,该表达式是单调递减的,因此我们可以构建一个函数,该函数对我们的目标方差过零,并通过简单的二分法解决这个问题。这是
第 2 步:
/**
* Find the displacement {@code x} for the given {@code stddev} by simple
* bisection.
* @param min support lower bound
* @param max support upper bound
* @param stddev the standard deviation we want
* @return the length {@code x} of {@code d - c = b - a} that yields a
* standard deviation roughly equal to {@code stddev}
*/
static double bisect(double min, double max, double stddev) {
final double eps = 1e-4;
final double var = pow(stddev, 2);
int iters = 0;
double a = eps;
double b = (max - min) / 2 - eps;
double x = eps;
double dx = b - a;
while (abs(dx) > eps && iters < 150 && eval(min, max, x, var) != 0.0) {
x = ((a + b) / 2);
if ((eval(min, max, a, var) * eval(min, max, x, var)) < 0.0) {
b = x;
dx = b - a;
} else {
a = x;
dx = b - a;
}
iters++;
}
if (abs(eval(min, max, x, var)) > eps) {
throw new RuntimeException("failed to find solution");
}
return x;
}
/**
* Function whose root we want to find.
*/
static double eval(double min, double max, double x, double var) {
return varSymTrapezoid(min, max, x) - var;
}
Run Code Online (Sandbox Code Playgroud)
bisect使用标准偏差 ( bisect(2.0, 10.0, 2.0))的所需值 2.0调用该方法为我们提供了所需的位移:~ 1.1716。既然知道了 的值,x我们要做的最后一件事就是构造一个适当分布的随机变量,它是
第 3 步:
概率论中的一个众所周知的事实是,两个独立均匀分布的随机变量之X1 ~ U[a1, b1]和X2 ~ U[a2, b2]是区间 [a1 + a2, b1 + b2] 上的对称梯形分布随机变量,条件是a1 + b2 < a2 + b1(case 1) 或a2 + b1 < a1 + b2(case 1) 2)。我们必须避免这种情况a2 + b1 = a1 + b2(情况 3),因为总和具有我们不想要的对称三角形分布。
我们将选择情况 1 ( a1 + b2 < a2 + b1)。在这种情况下, 的长度b2 - a2将等于“位移” x。
因此,我们所要做的就是选择区间边界 a1、a2、b1 和 b2,使得a1 + a2 = min、b1 + b2 = max、b2 - a2 = x和上述不等式成立:
/**
* Return a pseudorandom double for the symmetric trapezoidal distribution
* defined by the triple {@code (min, max, x)}
* @param min support lower bound
* @param max support upper bound
* @param x length of {@code max - c = b - min}, constrained to lie in the
* open interval {@code (0, (max-min)/2)}
*/
public static double symTrapezoidRandom(double min, double max, double x) {
final double a1 = 0.5 * min;
final double a2 = a1;
final double b1 = max - a2 - x;
final double b2 = a2 + x;
if ((a1 + b2) >= (a2 + b1)) {
throw new IllegalArgumentException();
}
double u = a1 + (b1 - a1) * rnd.nextDouble();
double v = a2 + (b2 - a2) * rnd.nextDouble();
return u + v;
}
Run Code Online (Sandbox Code Playgroud)
symTrapezoidRandom(2.0, 10.0, 1.1716)重复调用可为您提供具有所需分布的随机变量。
您可以使用其他更复杂的发行版(例如Beta )执行非常相似的操作。这将为您提供其他(通常更灵活)的可接受差异范围,但您需要一个像commons.math这样的 3rd 方库。
abs, pow,sqrt在代码中是指静态导入的 java.lang.Math 方法,rnd是 java.util.Random 的一个实例。
| 归档时间: |
|
| 查看次数: |
1307 次 |
| 最近记录: |