今回のエンジニアブログの担当は大橋です。
好きな乱数生成法はXorshiftです。
よろしくおねがいします。
さて、今回はUnityの乱数に関してです。
Unityには乱数を得るたものクラス(UnityEngine.Random)が用意されていますが、
これで得られる乱数は果たしてちゃんとランダムなのか気になったので、
ちょっと見てみました。
注)きちんとした乱数性能の評価ではないです。
Unityのバージョンは4.6.1f1、スクリプトはC#です。
ちゃんとしたランダムって?
プログラムで使われる乱数は、正確には擬似乱数といって、真の乱数ではありません。
計算式によってランダムっぽい数列を求めているだけです(だけ、というと怒られそうですが)。
この擬似乱数の求め方にはいろいろな方法があるのですが、その性能も様々です。
例えば、性能が悪い方法を使うと、奇数と偶数が交互に出てきたりするので油断なりません。
ただ、乱数として性能が悪くても、計算速度が速かったり、実装がしやすかったりするので、
一概に乱数性能が悪いからダメだとは言えませんが、
でもさすがに、奇数と偶数が交互に出てきたりしたら困ります。
乱数を画像化してみる①
ということで、Unityで得られる乱数が明らかに性能が悪い乱数じゃないか見てみようと思います。
画像上にランダムでドットを打ってみる
まずは、サイズが256x256の画像上にランダムで黒いドットを打ってみます。
つまり、0〜255の範囲の乱数を2つ求め、それをX座標とY座標として、その位置に黒いドットを打つことを繰り返します。
こうして作った画像を見たときに、なんらかのドットのパターンが浮かび上がると、
このときの乱数に規則性があるということなので、乱数としてはあまりよくありません。
ソースコード
// 乱数値をテクスチャ上にプロット Texture2D tex = new Texture2D(256, 256, TextureFormat.ARGB32, false); Color color = new Color(0f, 0f, 0f, 1f); for (int i = 0; i < 20000; i++) { int x = UnityEngine.Random.Range (0, 256); // 0〜255の乱数をX座標にする int y = UnityEngine.Random.Range (0, 256); // 0〜255の乱数をY座標にする tex.SetPixel (x, y, color); // 黒いドットを打つ } // テクスチャをpngファイルとして保存 byte [] png = tex.EncodeToPNG(); string filePath = EditorUtility.SaveFilePanel("Save", "", "Random.png", "png"); if (filePath.Length > 0) { File.WriteAllBytes(filePath, png); }
結果
この画像からは規則的なパターンが見受けられません。
つまり、だいたいランダム!
乱数を画像化してみる②
先ほどとは違う方法でも画像を作ってみます。
今度は、画像の1ピクセルごとに0か1の乱数を求めて、1のときだけ黒いドットを打ってみます。
ソースコード
// 乱数値をテクスチャ上にプロット Texture2D tex = new Texture2D(256, 256, TextureFormat.ARGB32, false); Color color = new Color(0f, 0f, 0f, 1f); for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { if (UnityEngine.Random.Range (0, 2) == 1) { // 0か1の乱数を求める tex.SetPixel (x, y, color); // 1のときだけドットを打つ } } } // テクスチャをpngファイルとして保存 // 省略
結果
こちらも規則的なパターンは見受けられません。
つまり、奇数と偶数が交互に現れたりすることなく、だいたいランダム!
乱数を画像化してみる③
最後にもう1つ。
「乱数を画像化してみる②」と同じように、画像の1ピクセルごとに0か1の乱数を求めて、1のときだけ黒いドットを打つのですが、
1行ごとに乱数のシードを+1させてみます。
ソースコード
// 乱数値をテクスチャ上にプロット Texture2D tex = new Texture2D(256, 256, TextureFormat.ARGB32, false); Color color = new Color(0f, 0f, 0f, 1f); for (int y = 0; y < 256; y++) { UnityEngine.Random.seed = y; // 1行ごとにシードを+1 for (int x = 0; x < 256; x++) { if (UnityEngine.Random.Range (0, 2) == 0) { // 0か1の乱数を求める tex.SetPixel (x, y, color); // 1のときだけドットを打つ } } } // テクスチャをpngファイルとして保存 // 省略
結果
パターンは見受けられません。めでたしめでたし。
System.Randomでもやってみる
UnityEngine.Randomではなく、.NetのSystem.Randomを使って乱数を求めた場合にどうなるかもやってみます。
ソースコードは、先のものとほぼ一緒なので省略。
乱数を画像化してみる①結果
パターンなし。
乱数を画像化してみる②結果
やはりパターンなし。
乱数を画像化してみる③結果
パターンが!
つまり、0か1の乱数を求める場合、シードを変えても同じような乱数になってしまうことがあると分かります。
この点では、UnityEngine.Randomの方が優秀ということです。
結論
UnityEngine.Randomはだいたいランダム!
冒頭にも書いた通り、これらはきちんとした乱数性能の検証ではないので、
ご興味をもたれた方はご自身でいろいろ調べていただけると幸いです。