提问人:Jason Ericson 提问时间:12/14/2021 最后编辑:SayseJason Ericson 更新时间:12/16/2021 访问量:530
具有单个目标和击中目标几率的加权随机浮点数
Weighted random float number with single target and chance of hitting target
问:
我正在尝试创建一个随机浮点生成器(范围为 0.0-1.0),我可以在其中提供单个目标值,以及增加或减少该目标被击中的机会的强度值。例如,如果我的目标是 0.7,并且我的强度值很高,我希望该函数返回的值主要在 0.7 左右。
换句话说,我想要一个函数,当运行很多次时,会产生一个分布图,如下所示:
是的,有点像钟形曲线,但有严格的范围限制(而不是正态分布的 -inf/+inf 范围限制)。钳制正态分布并不理想,我希望分布自然地在范围限制处结束。
我一直在尝试的方法是提出一个公式,将值从均匀分布转换为我所设想的神话分布。类似于反正弦波的东西:
能够通过强度值扩大中间点:
以及通过目标值上下移动中点的能力:
目标更改为 0.7(由 MS Paint 提供,因为我无法从数学上弄清楚这部分)
这个理论上的“强度值”的范围有待商榷。我可以想象一个有限的值,比如在 0 到 1 之间,其中 0 表示它是均匀分布的,1 表示它有 100% 的机会击中目标;或者,我可以想象一个值越高,它就越接近 100% 的机会,但从未达到它。沿着任何一条线的东西都会起作用。
我正在使用 C#,但这可能与语言无关。任何为我指明正确方向的帮助都是值得赞赏的。也很乐意进一步澄清。
答:
我不是数学家,但我看了一下,我觉得我得到了一些可能对你有用的东西。
我所做的只是采用正态分布公式:并使用 0.7 作为 mu 将分布向 0.7 移动。我添加了一个前导系数 0.623,将值移动到 0 到 1 之间,并将其从公式迁移到 C#,这可以在下面找到。
用法:
DistributedRandom random = new DistributedRandom();
// roll for the chance to hit
double roll = random.NextDouble();
// add a strength modifier to lower or strengthen the roll based on level or something
double actualRoll = 0.7d * roll;
定义
public class DistributedRandom : Random
{
public double Mean { get; set; } = 0.7d;
private const double limit = 0.623d;
private const double alpha = 0.25d;
private readonly double sqrtOf2Pi;
private readonly double leadingCoefficient;
public DistributedRandom()
{
sqrtOf2Pi = Math.Sqrt(2 * Math.PI);
leadingCoefficient = 1d / (alpha * sqrtOf2Pi);
leadingCoefficient *= limit;
}
public override double NextDouble()
{
double x = base.NextDouble();
double exponent = -0.5d * Math.Pow((x - Mean) / alpha, 2d);
double result = leadingCoefficient * Math.Pow(Math.E,exponent);
return result;
}
}
编辑: 如果您不是在寻找与您提供的分布直方图相似的输出,而是想要与您绘制的 sigmoid 函数更相似的东西,我创建了一个替代版本。
感谢 Ruzihm 指出这一点。
我继续使用 CDF 进行正态分布:其中定义为错误函数: 。我添加了一个系数来缩放输出以将其保持在 0d - 1d 之间。erf
1.77
它应该生成类似于下面的数字:
在这里,您可以找到备用课程:
public class DistributedRandom : Random
{
public double Mean { get; set; } = 0.7d;
private const double xOffset = 1d;
private const double yOffset = 0.88d;
private const double alpha = 0.25d;
private readonly double sqrtOf2Pi = Math.Sqrt(2 * Math.PI);
private readonly double leadingCoefficient;
private const double cdfLimit = 1.77d;
private readonly double sqrt2 = Math.Sqrt(2);
private readonly double sqrtPi = Math.Sqrt(Math.PI);
private readonly double errorFunctionCoefficient;
private readonly double cdfDivisor;
public DistributedRandom()
{
leadingCoefficient = 1d / (alpha * sqrtOf2Pi);
errorFunctionCoefficient = 2d / sqrtPi;
cdfDivisor = alpha * sqrt2;
}
public override double NextDouble()
{
double x = base.NextDouble();
return CDF(x) - yOffset;
}
private double DistributionFunction(double x)
{
double exponent = -0.5d * Math.Pow((x - Mean) / alpha, 2d);
double result = leadingCoefficient * Math.Pow(Math.E, exponent);
return result;
}
private double ErrorFunction(double x)
{
return errorFunctionCoefficient * Math.Pow(Math.E,-Math.Pow(x,2));
}
private double CDF(double x)
{
x = DistributionFunction(x + xOffset)/cdfDivisor;
double result = 0.5d * (1 + ErrorFunction(x));
return cdfLimit * result;
}
}
评论
1.77d * 0.5d * (1+erf((x-0.7d)/(0.25d * sqrt(2))))-1
我想出了一个可行的解决方案。这并不像我所期望的那么优雅,因为它每个结果需要 2 个随机数,但它绝对满足了要求。基本上,它采用一个随机数,使用另一个向 1 呈指数弯曲的随机数,然后向目标方向弯曲。
我用 python 写了它,因为我更容易可视化它的直方图:
import math
import random
# Linearly interpolate between a and b by t.
def lerp(a, b, t):
return ((1.0 - t) * a) + (t * b)
# What we want the median value to be.
target = 0.7
# How often we will hit that median value. (0 = uniform distribution, higher = greater chance of hitting median)
strength = 1.0
values = []
for i in range(0, 1000):
# Start with a base float between 0 and 1.
base = random.random()
# Get another float between 0 and 1, that trends towards 1 with a higher strength value.
adjust = random.random()
adjust = 1.0 - math.pow(1.0 - adjust, strength)
# Lerp the base float towards the target by the adjust amount.
value = lerp(base, target, adjust)
values.append(value)
# Graph histogram
import matplotlib.pyplot as plt
import scipy.special as sps
count, bins, ignored = plt.hist(values, 50, density=True)
plt.show()
目标 = 0.7,强度 = 1
目标 = 0.2,强度 = 1
目标 = 0.7,强度 = 3
目标 = 0.7,强度 = 0
(这是均匀分布 - 它可能看起来有点锯齿状,但我测试过,这只是 python 的随机数生成器。
评论
if base < .5: base = base/.5 * target else: base = target + (base-.5)/.5 * (1-target)
上一个:函数返回点和效率
评论