如何获得k到h范围内的随机数,使得数字越接近h,它就越不可能出现?
我需要20到1980年之间的数字。
答案 0 :(得分:4)
我在Eclipse中尝试过一些东西,这里有结果。
interface Generator {
double generate(double low, double high);
}
abstract class AbstractGenerator implements Generator {
protected final Random rand;
public AbstractGenerator()
{
rand = new Random();
}
public AbstractGenerator(long seed)
{
rand = new Random(seed);
}
}
现在有各种生成器实现的结果:
我试图在0到9的等级上生成10万个数字,这里它们显示为条形。
class Catan2 extends AbstractGenerator {
@Override
public double generate(double low, double high)
{
return low + (high - low) * Math.abs(-1 + (rand.nextDouble() + rand.nextDouble()));
}
}
Reusults:
0 : *******************
1 : ******************
2 : ****************
3 : **************
4 : ************
5 : *********
6 : *******
7 : *****
8 : ***
9 : *
class Catan3 extends AbstractGenerator {
@Override
public double generate(double low, double high)
{
return low + (high - low) * Math.abs(-1.5 + (rand.nextDouble() + rand.nextDouble() + rand.nextDouble())) / 1.5;
}
}
Reusults:
0 : ***********************
1 : *********************
2 : *******************
3 : ***************
4 : ***********
5 : *******
6 : *****
7 : ***
8 : *
9 : *
class Catan4 extends AbstractGenerator {
@Override
public double generate(double low, double high)
{
return low + (high - low) * Math.abs(-2 + (rand.nextDouble() + rand.nextDouble() + rand.nextDouble() + rand.nextDouble())) / 2D;
}
}
结果:
0 : ***************************
1 : ************************
2 : ********************
3 : **************
4 : *********
5 : *****
6 : ***
7 : *
8 : *
9 : *
我认为“Catan 3”是其中最好的。
公式为:low+(high-low)*abs(-1.5+(RAND+RAND+RAND))/1.5
基本上,我得到一个“山丘”分布,然后我居中并取其绝对价值。然后我将其标准化为所需的值。
答案 1 :(得分:3)
还有另一种选择。有一些标准方法可以在高斯分布上产生随机数。设置高斯RNG,平均值为k,标准差为h / 5。拒绝低于k的任何数字(约为生成数字的一半)并拒绝所有大于h的数字(5%或更少)。
如果要优化结果,可以调整标准偏差。实际上,这是具有截尾的半高斯RNG,因此数字不是线性的;你会比k更接近k。
ETA:感谢@ MightyPork的评论,让我思考。高斯分布是对称的,因此不需要丢弃任何小于k的原始值。只需将它们从k下方移到k以上相同的距离:
if (raw < k)
raw <- k + (k - raw)
end if
仍然需要拒绝高于h的值。
答案 2 :(得分:2)
假设我们的范围是[0,4],创建一个这样的数组:
[000001111222334]
现在使用标准Random
对象从数组中绘制。通过这样做,我们已经从均匀分布的绘制转变为我们自己设计的分布。实际上,我们不打算使用辅助阵列。您可以执行以下操作来代替辅助数组:
从[0,14]画出;将[0,4]映射到0,[5,8]到1,[9,11]到2,[12,13]到3和[14]到4。
这实际上取决于您的发行版。您可以通过在不同范围内从均匀分布中多次绘制来从非均匀分布中近似绘制。当然,如果您知道分布的概率质量函数或概率密度函数,那么您就是金色。
答案 3 :(得分:1)
如果你需要很好地控制数字的分布,那么一个好的方法就是反转方法。创建一个(x,y)对的排序表,其中x和y都单调增加:x从0到1,y从你需要的伪随机数的低到高值。算法是:
x = uniform random float in [0..1)
Search the table to find (x[i],y[i]) such that x[i] <= x < x[i+1]
// Return linearly interpolated y value
return y[i] + (x - x[i]) / (x[i+1] - x[i]) * (y[i+1] - y[i])
您可以使用表条目控制返回值的分布。
如果表只包含(0,0)和(1,1),那么显然返回值等于x,并且分布是均匀的。要获得更高的数字,请描述一条曲线,该曲线在开始时会更快地增加,而在更高的x值处更平坦,例如:
(0,0) (0.25,0.5) (1,1)
你应该能够理解为什么会这样。在均匀分布中,一半的数字在0到0.5之间。使用此表,只有四分之一的数字在该范围内,因此其他四分之三的数字在0.5到1之间。根据需要,高数字更频繁。
只要它单调增加,您就可以创建任意曲线和任何形状的曲线。如果表格有多对,请考虑二进制搜索速度。
对于20到1980的范围,相应的表格类似于:
(0, 20) (0.25, 1000) (1, 1980)
如果您需要整数,则需要使用
(0, 20) (0.25, 1000) (1, 1981)
然后从结果中截断分数。
同样,您可能希望表中有更多的点来使ICDF更加平滑。这只是为了说明。
数学
存储在表中的曲线称为返回的伪随机数的反向累积密度函数(ICDF)。概率分布函数(PDF)是非负函数,其曲线下面积为1.常用的PFD是均匀的,指数的和正态的。相应的CDF是PDF的运行积分。 ICDF与CDF相反。众所周知,要使用任何给定的PDF生成随机数,您可以找到ICDF并应用上述算法。