【发布时间】:2013-11-06 01:42:16
【问题描述】:
C# 的 Random 不够随机
我的目标是获得比使用内置 Random 或加密 RNG 可能实现的更多随机性。
问:如何从随机数生成器中获得更多随机性?
首先,我正在处理一个 Random 实例。
我正在使用此例程为对象分配随机速度(XNA Vector2):
public static double d2r = Math.PI / 180f;
public static Vector2 rndV2gtHalf(float scaleFactor)
{
double dir = random.NextDouble() * 360 * d2r;
return new Vector2((float)Math.Cos(dir) * scaleFactor, (float)Math.Sin(dir) * scaleFactor);
}
d2r 是角度到弧度转换的一个因子。出于测试目的,我每秒在屏幕上放置大约 6000 个对象,它们从我的鼠标位置以“随机”方向移动。这是结果
虽然这些物体朝各个方向飞去,但它们似乎是在“摆动”的连接流中。我猜测摆动是流本身的“箱”内微小随机性的结果。溪流不围绕中心旋转,因此即使摆动,覆盖范围也不完整。
我试图通过使其依赖于 random.NextDouble 的 3 次使用来增加我的速度的随机性。这导致接近 0 度的大片带几乎是空的。除了那条空旷的地带,其余的方向都像以前一样被覆盖了。
public static Vector2 rndV2gtHalf(float scaleFactor)
{
double dir = ((random.NextDouble() + random.NextDouble() + random.NextDouble()) / 3) * 360 * d2r;
return new Vector2((float)Math.Cos(dir) * scaleFactor, (float)Math.Sin(dir) * scaleFactor);
}
显然,这不会产生更多的随机性。
所以,我想我可能在这个过程中过早地组合了随机数,并且通过添加从根本上改变了数字的随机性。所以,在转换为弧度之前,我尝试对其中的一些进行修改。
public static Vector2 rndV2gtHalf(float scaleFactor)
{
double dir = random.NextDouble() * 360;
dir += random.NextDouble() * 360;
dir += random.NextDouble() * 360;
dir += random.NextDouble() * 360;
dir += random.NextDouble() * 360;
dir += random.NextDouble() * 360;
dir %= 360;
dir *= d2r;
return new Vector2((float)Math.Cos(dir) * scaleFactor, (float)Math.Sin(dir) * scaleFactor);
}
这似乎并不比依赖单个 random.NextDouble 更好。
我在某处读到有一个更好的加密随机数 gen,但它似乎不具备产生 Double-s 的本机能力,所以我尝试将它们组合起来。
public static RandomNumberGenerator rng = new RNGCryptoServiceProvider();
public static Vector2 rndV2gtHalf(float scaleFactor)
{
byte[] tokenData = new byte[2];
rng.GetBytes(tokenData);
int myInt = tokenData[1] << 8 + tokenData[0];
double myDouble = ((double)myInt) / 65536f;
double dir = myDouble * 360 * d2r;
return new Vector2((float)Math.Cos(dir) * scaleFactor, (float)Math.Sin(dir) * scaleFactor);
}
这导致粒子的覆盖更加随意。
考虑到两个字节的数据可能不够,我把它增加到了 4 个:
byte[] tokenData = new byte[4];
rng.GetBytes(tokenData);
int myInt = tokenData[3]<<24 + tokenData[2]<<16 + tokenData[1]<<8 + tokenData[0];
double myDouble = ((double)myInt) / 4294967296;
这并没有让它变得更好。
好的。所以也许这与我使用 sin/cos 的方式有关。我将 fn 修改为更像这样:
public static Vector2 rndV2gtHalf(float scaleFactor)
{
return scaleFactor *
new Vector2((float)(random.NextDouble() - 0.5f), (float)(random.NextDouble() - 0.5f));
}
除了生成一个长到屏幕边缘的方形流场之外,它看起来不像 sin/cos 尝试那么随机。
我尝试的任何方法似乎都不起作用。那么,重复我的问题,我怎样才能从随机数生成器中获得更多随机性?
【问题讨论】:
-
如果您以数字而不是图片的形式表达您的担忧,也许会有所帮助。
-
您基本上是在随机进行(哈哈),而没有真正从逻辑上考虑问题。例如,考虑将三个随机数相加并取其平均值的想法“更随机”——即更少均匀随机。当你一遍又一遍地滚动一个骰子时,你会得到一个到六个均匀分布的骰子。当你掷两个骰子并取平均值时,你会得到 3.5 17% 的时间,但只有 3% 的时间得到 1。您使分布得到的 less 的总和越多;这就是中心极限定理。
-
鉴于当您更改随机算法时显示的形状几乎没有变化,所以问题可能首先不在随机算法中。您可能在代码中的其他地方存在未向我们展示的错误。
-
Eric:我同意,无论是关于我的挥舞,还是关于平均的影响。尽管如此,它还是添加然后删除了一个 dbl(再次更改范围),这给了我更多随机结果。最奇怪的部分是它是在 scaleFactor 上,而不是在角度上。那么,关于您的评论,实际应用是什么——应用以使 RNG 更加随机的逻辑思维是什么?关于其他地方的错误,除了初始速度的设置外,在每次更新时,都会将速度添加到位置。就是这样。
-
但如果你不相信我,这是我使用的代码(抱歉,由于在 cmets 中未格式化): public override void move(ImageObj obj, GameTime gameTime) { obj.Location += 对象速度; if ( (obj.RectScaled.Right G.screenSize.X) | (obj.RectScaled.Bottom G.screenSize.Y) ) { obj.alive = false; } }