ランダム値

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型配列の各要素にランダムな値を代入します。
上のコードでは結果を見やすくするためにBitConverter.ToStringメソッドで16進数表示にしていますが、byte型なので中身は0~255までの数値です。
(byte.MaxValue以下)

シード

Randomクラスのインスタンス生成時の引数(コンストラクタという)に数値(int型)を渡すことができます。
この値をシード(乱数の種)といいます。
シードが同じ値であるインスタンスは、生成される乱数が同じになります。

コンストラクタを指定しない場合、自動的にEnvironment.TickCountという値がシードに使用されます。
これはシステム(パソコンなど)が起動してからの時間(ミリ秒)を示す整数値(int型)です。
そのため、例えば以下のようなコードは乱数の生成としては適当ではありません。


Random r1 = new Random();
Random r2 = new Random();

//r1もr2も同じシードとなっている可能性が高い
//なので同じ値が生成される可能性大
Console.WriteLine(r1.Next(100));
Console.WriteLine(r2.Next(100));

Randomクラスのインスタンスを二つ生成していますが、このようなコードではインスタンスの生成から次のインスタンス生成までに1ミリ秒も掛かることはなく、全く同じシードが使用される可能性が高いです。
するとこの二つのインスタンスは同じランダム値を生成します。

同じプログラムを全く同時に起動することはあまりないため、複数のインスタンスを生成せず、ひとつを使いまわせばこのような問題は発生しません。
しかし例えばマルチスレッドプログラムなどではスレッド毎にRandomクラスのインスタンスが欲しい場合があります。
複数のRandomクラスのインスタンスを使用する場合はシードに適当な値を指定します。


Random r1 = new Random(1);
Random r2 = new Random(2);

Console.WriteLine(r1.Next(100));
Console.WriteLine(r2.Next(100));

これでr1とr2は別のランダム値を生成するようになります。
しかしこのコードは次にプログラムを実行した時もシードが同じであるため、全く同じランダム値を生成します。
それでは都合が悪いことがほとんどだと思うので、Environment.TickCountで起動毎に異なるシードを指定します。


Random r1 = new Random(Environment.TickCount + 1);
Random r2 = new Random(Environment.TickCount + 2);

逆に、何らかのテスト用のプログラムなどではシードを固定することで毎回同じランダム値を得ることができます。

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型は32bit環境では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以下)