当我刚接触 C# 时,我真的不想长时间使用 Random() 来尝试得到我想要的东西,而且我也想知道它做了我需要它做的事情,而不需要花很长时间测试的时间量。我怎样才能用随机数得到 0.5% 的概率(1/200)?这段代码会吗?随机到底有多随机……这不是“随机有多随机”的问题,所以不要发布重复项,而是我如何做到这一点的问题。
我的问题不是“随机性如何,而是这段代码是否是完成这项工作的最佳方式,以及它能否实现我想要实现的目标。”
var random = new Random();
var randomNumber = random.Next(1, 200);
if (randomNumber == 87)
{
// I can put any number inbetween 1 and 200, will this work?
// If we reach this if statement we have got a 0.5 chance?
}
Run Code Online (Sandbox Code Playgroud)
首先,您应该将机会转换为 0.0 到 1.0 之间的标准化值。这是概率的数学概念。
对于你的情况,这会给你double probability = 0.005;。
然后您可以执行以下操作:
if (rng.NextDouble() < probability)
{
...
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为Random.NextDouble()返回一个在半开区间内均匀分布的随机数[0.0, 1.0)(即最多但不包括1.0。)
因此,如果您的概率是,0.0则 的主体if将永远不会被执行,而如果您的概率是1.0,则它将始终被执行。
使用归一化概率的优点是它适用于任何概率,而不仅仅是积分概率。
如果您碰巧有百分比概率,则可以非常简单地将其转换为标准化概率 - 通过将其除以100.0。
附录:
使用它Random.Next(int min, int max)几乎没有什么好处,因为它只适用于积分概率。在幕后,Random.Next(int min, int max)是这样实现的:
public virtual int Next(int minValue, int maxValue) {
if (minValue>maxValue) {
throw new ArgumentOutOfRangeException("minValue",Environment.GetResourceString("Argument_MinMaxValue", "minValue", "maxValue"));
}
Contract.EndContractBlock();
long range = (long)maxValue-minValue;
if( range <= (long)Int32.MaxValue) {
return ((int)(Sample() * range) + minValue);
}
else {
return (int)((long)(GetSampleForLargeRange() * range) + minValue);
}
}
Run Code Online (Sandbox Code Playgroud)
并NextDouble()实现为:
public virtual double NextDouble() {
return Sample();
}
Run Code Online (Sandbox Code Playgroud)
请注意,这两个实现都调用Sample().
最后我只想指出,内置的 Random 类并不是特别好 - 它的周期不是很长。我使用基于 128 位XOR 移位的 RNG ,它速度非常快,并且生成非常“好的”随机数。
(我使用一个基于此 XORSHIFT+ 生成器的生成器。)