C#のランダムは十分にランダムではありません
私の目標は、組み込みのランダムまたは暗号化 RNG のいずれかを使用して可能と思われるよりも多くのランダム性を取得することです。
Q: 乱数ジェネレーターからランダム性を高めるにはどうすればよいですか?
まず、Random の 1 つのインスタンスを扱っています。
このルーチンを使用して、ランダムな速度 (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 に依存するよりも優れているようです。
より優れた暗号ランダム num 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);
}
これにより、パーティクルがさらに無計画にカバーされます。
たぶん2バイト分のデータが足りないと思い、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 の試みよりもランダムに見えなくなります。
私が試みたものは何もうまくいかないようです。それで、私の質問を繰り返しますが、どうすれば乱数ジェネレーターからより多くのランダム性を得ることができますか?