ランダム値
Randomクラス
プログラミングではランダムな値(乱数)を使用することがよくあります。
C#で乱数を得るにはRandomクラスを使用します。
(System
名前空間)
乱数を生成するメソッド(関数)はRandomクラスのインスタンスメソッドとして用意されています。
Randomクラスのインスタンスはnew Random()
という形で作ることができます。
//Randomクラスのインスタンス生成
Random r = new Random();
このページのコードの実行結果はプログラムの実行毎に異なります。
整数のランダム値
整数のランダム値はNext
メソッドで生成します。
Random r = new Random();
//0以上のランダムな値
int randomValue1 = r.Next();
//0以上10未満のランダムな値
int randomValue2 = r.Next(10);
//100以上1000未満のランダムな値
int randomValue3 = r.Next(100, 1000);
Console.WriteLine(randomValue1);
Console.WriteLine(randomValue2);
Console.WriteLine(randomValue3);
1420795739 8 711
Next
メソッドに引数を指定しない場合は0以上、int.MaxValue
未満の値が生成されます。
(0~2147483647未満)
引数をひとつ指定すると、0以上、第一引数未満の値が生成されます。
引数をふたつ指定すると、第一引数以上、第二引数未満の値が生成されます。
いずれの場合も最大値は指定の値未満となることに注意してください。
小数のランダム値
小数のランダム値が欲しい場合はNextDouble
メソッドを使用します。
Random r = new Random();
//0以上1未満のランダムな小数
double randomValue1 = r.NextDouble();
//0以上10未満のランダムな小数
double randomValue2 = r.NextDouble() * 10;
//-1以上、1未満のランダムな小数
double randomValue4 = r.NextDouble() * 2 - 1;
NextDouble
メソッドには引数がなく、0.0以上~1.0未満の値をdouble型で返します。
これ以外の範囲の乱数が欲しい場合は戻り値を自前で加工します。
Nextメソッドで小数値を得る
ちなみにNext
メソッドを使って小数のランダム値を得ることもできます。
Random r = new Random();
//0以上1未満のランダムな小数
double randomValue1 = r.Next() / (double)int.MaxValue;
//0以上1以下のランダムな小数
double randomValue2 = r.Next() / (double)(int.MaxValue - 1);
Next
メソッドは0からint.MaxValue
より小さい値を返すため、Next
メソッドの戻り値をint.MaxValue
で割れば0以上1未満のランダムな小数値となります。
「int.MaxValue - 1」で割れば0以上1以下のランダムな小数値が得られます。
ただしint型同士の演算の結果はint型になるので、どちらか一方をdoubleやfloatなどにキャストする必要があります。
ランダムなバイト配列
ランダムなバイト配列を得るにはNextBytes
メソッドを使用します。
これは認証に使うデータなどでランダムなバイト値が欲しい場合に使用します。
Random r = new Random();
byte[] bytes = new byte[4];
r.NextBytes(bytes);
Console.WriteLine(BitConverter.ToString(bytes));
foreach (var b in bytes)
Console.Write("{0} ", b);
8F-DC-51-5B 143 220 81 91
NextBytes
メソッドは引数に指定されたbyte型配列の各要素にランダムな値を代入します。
byte型なので要素は0~255までの数値が格納されます。
(byte.MaxValue
以下)
ちなみに上のコードでは結果を見やすくするためにBitConverter.ToString
メソッドで16進数表記にしたものと、数値をそのまま出力したものとを表示しています。
シード
Randomクラスのインスタンス生成時の引数(コンストラクターという)に数値(int型)を渡すことができます。
この値をシード(乱数の種)といいます。
シードが同じ値であるインスタンスは、生成される乱数が同じになります。
コンストラクターを指定しない場合、自動的にEnvironment.TickCount
という値がシードに使用されます。
(.NET Frameworkの場合。.NET Coreについては後述)
これはシステム(パソコンなど)が起動してからの時間(ミリ秒)を示す整数値(int型)です。
そのため、例えば以下のようなコードは乱数の生成としては適当ではありません。
Random r1 = new Random();
Random r2 = new Random();
//r1もr2も同じシードとなっている可能性がある
//その場合は同じ値が生成される
Console.WriteLine(r1.Next(100));
Console.WriteLine(r2.Next(100));
このコードの二つのインスタンスの生成はほぼ同時に行われ、全く同じシード(ミリ秒)が使用される可能性があります。
その場合、シードが同じであるこの二つのインスタンスは同じランダム値を生成するため、期待通りの動作になりません。
同じプログラムを全く同時に起動することはあまりないため、複数のインスタンスを生成せず、ひとつを使いまわせばこのような問題は発生しません。
しかし例えばマルチスレッドプログラムなどではスレッド毎にRandomクラスのインスタンスが欲しい場合があります。
複数のRandomクラスのインスタンスを使用する場合はシードに適当な値を指定します。
Random r1 = new Random(1);
Random r2 = new Random(2);
Console.WriteLine(r1.Next(100));
Console.WriteLine(r2.Next(100));
これでr1
とr2
は別のランダム値を生成するようになります。
しかしこのコードはシードが「1」や「2」で固定であるため、次にプログラムを実行した時も全く同じランダム値を生成します。
それでは都合が悪いことがほとんどだと思うので、Environment.TickCount
で起動毎に異なるシードを指定します。
Random r1 = new Random(Environment.TickCount + 1);
Random r2 = new Random(Environment.TickCount + 2);
逆に、何らかのテスト用のプログラムなどではシードを固定することで毎回同じランダム値を得ることができます。
.NET Core環境では、シードを指定しないでRandomクラスのインスタンスを生成する場合はEnvironment.TickCount
ではなく、内部の乱数が使用されます。
そのため、続けてRandomインスタンスを生成した場合でも異なるシードが使用されるので、上記のような問題は起こりません。
RNGCryptoServiceProviderクラス
Randomクラスで生成される乱数は疑似乱数といい、精度の悪い乱数です。
通常のプログラムでは特に困りませんが、例えば暗号化の目的などで使用することは推奨されません。
(精度の悪い乱数は、推測が比較的容易であるためセキュリティ上の問題となる場合がある)
RNGCryptoServiceProviderクラスは、精度の高い乱数を生成します。
(System.Security.Cryptography
名前空間)
使い方はRandomクラスと基本的に同じですが、メソッドはNextBytes
とほぼ同じであるGetBytes
メソッドを使用します。
System.Security.Cryptography.RNGCryptoServiceProvider rng
= new System.Security.Cryptography.RNGCryptoServiceProvider();
byte[] bytes = new byte[1];
rng.GetBytes(bytes);
//最後にDisposeメソッドを呼ぶ
rng.Dispose();
Console.WriteLine(bytes[0]);
Console.ReadLine();
42
引数に指定したbyte型配列の各要素に、ランダムな値が代入されます。
RNGCryptoServiceProviderクラスは、最後にDispose
メソッドを呼ぶことが推奨されます。
Dispose
メソッドはインスタンスを破棄し、メモリを解放します。
Dispose
メソッドの実行後はそのインスタンスは使用できません。
(詳しくはオブジェクトの破棄を参照)
RNGCryptoServiceProviderクラスはRandomクラスに比べて低速なので、通常はRandomクラスで十分です。
GetNonZeroBytes
GetNonZeroBytes
メソッドは基本的にGetBytes
メソッドと同じですが、ゼロを含まないランダムな値を生成します。
var rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
byte[] bytes = new byte[1];
rng.GetNonZeroBytes(bytes);
rng.Dispose();
Console.WriteLine(bytes[0]);
Console.ReadLine();
別のデータ型のランダム値を生成する
RNGCryptoServiceProviderクラスにはbyte型を生成するメソッドしかありません。
別のデータ型のランダム値を生成する場合は、そのデータ型のバイト数分のランダム値を生成してから変換します。
例えばint型は4バイトのデータ型なので、要素数4のbyte型配列にランダム値を生成します。
そしてBitConverterクラスのToInt32
メソッドを使用してバイト配列を目的のデータ型に変換します。
var rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
byte[] bytes = new byte[4];
rng.GetBytes(bytes);
rng.Dispose();
int randomValue = BitConverter.ToInt32(bytes, 0);
Console.WriteLine(randomValue);
この方法で生成した値は、そのデータ型が取り得る範囲のランダム値となります。
(○○.MinValue以上~○○.MaxValue以下)