乱数について
c#の乱数の作成について気をつけておくことがあります。
一般のプログラムで使われる乱数
0から99までの乱数を50個作成するプログラム
次のプログラムは、高速で、新しい乱数を作成するプログラムになります。
for (int i = 0; i < 50; i++) { var rnd = new Random(); Console.Write($"{rnd.Next(100)} "); Console.WriteLine(Environment.TickCount); }
Environment.TickCountについて // 概要: // システム起動後のミリ秒単位の経過時間を取得します // 戻り値: // コンピューターが最後に起動してからの経過時間(ミリ秒(int型))
実行結果
TickCountが同じ場合、同じ乱数が生成されています
37 18134312 37 18134312 37 18134312 ・ ・ 同じ結果が続きます ・ 37 18134312 37 18134312 72 18134328 72 18134328 ・ ・ 同じ結果が続きます ・ 72 18134328 72 18134328 72 18134328 ・ ・ ・
同じ乱数が生成される理由
1ミリ秒の間に作成されたインスタンスは、同じ乱数が生成されます。
これは、乱数が種(インスタンス化されるときの引数)をベースに作成されるので、同じ種でインスタンスが作成された場合、乱数も同じものが生成されるためです。
new Random();のように引数のないコンストラクタで作成すると、種は次のようにセットされます。
- .Net Framework (.Net 4.7.2) の場合
Environment.TickCount - .Net Core (.Net Core 2.2) の場合
内部で乱数を生成して種としている
.Net Frameworkのように1ミリ秒以内にインスタンスが作られても同じ乱数とならない
対応策
インスタンスを都度作成しないようにすればいいです。
rnd.Next(100)は乱数の種が変わらなければ、次に新しい乱数を取得できますので、インスタンスの作成を1度きりにすることで対応します。
var rnd = new Random(); for (int i = 0; i < 50; i++) { Console.Write($"{rnd.Next(100)} "); Console.WriteLine(Environment.TickCount); }
実行結果
TickCountが同じ場合でも、乱数がきちんと生成されています
55 24564531 54 24564531 92 24564531 4 24564531 65 24564531 32 24564546 54 24564546 50 24564546 70 24564546 ・ ・ 省略(違う結果) ・ 80 24564546 71 24564546 8 24564546 98 24564546 43 24564546 87 24564562 42 24564562 80 24564562 85 24564562 81 24564562 29 24564578 83 24564578
精度の高い乱数を取得する方法
暗号化に使用する場合など精度を要求される場合、RNGCryptoServiceProviderを使い乱数を生成することができます。
参考
0から99までの乱数を50個作成するプログラム
次のusingが必要
using System.Security.Cryptography;
RNGCryptoServiceProvider()は利用後にクローズする必要があるため、usingブロックを使います。
using (var rng = new RNGCryptoServiceProvider()) { // int型は4byteのため、すべてのデータが格納できるように確保します。(仕様) var randomByte = new byte[4]; for (int i = 0; i < 50; i++) { int rand; while (true) { // byte[]型で乱数を取得 rng.GetBytes(randomByte); // byte[]型をint型に変換 rand = BitConverter.ToInt32(randomByte, 0); if (rand > 0) break; } Console.Write($"{rand % 100} "); Console.WriteLine(Environment.TickCount); } }
実行結果
TickCountが同じ場合でも、乱数がきちんと生成されています
27 30304781 40 30304781 96 30304781 ・ ・ 省略(違う結果) ・ 23 30304781 50 30304781 86 30304781 41 30304781 40 30304796 41 30304796 40 30304796 ・ ・ 省略(違う結果) ・ 46 30304796 6 30304796 48 30304796 9 30304796 34 30304812 45 30304812 83 30304812 89 30304812 96 30304812 65 30304812 82 30304812 44 30304812 70 30304812
同じ乱数を発生させないサンプル(乱数の種をランダムにセットする方法)
sizeof(int)はint型の大きさ(サイズ)を取得します。
=4byteです。
次のコードは0から2までの精密な乱数を作成します。
Randomクラスのインスタンス生成時の乱数の種として、時計を使わずに精密に生成された乱数を使っています。
var randomByte = new byte[sizeof(int)]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(randomByte); } var rand = BitConverter.ToInt32(randomByte, 0); var random = new Random(rand); Console.Write($"{random.Next(3)} ");
ディスカッション
コメント一覧
まだ、コメントがありません