乱数を生成するには、Randomクラス(System名前空間)を使います。Randomクラスは、Donald E. Knuthの乱数ジェネレータ減算アルゴリズムの改訂版に基づいており、疑似乱数を生成します。
補足:.NET Framework 3までのMSDNには「The current implementation of the Random class is based on Donald E. Knuth's subtractive random number generator algorithm.」と書かれていますが、3.5からは「The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm.」となっており、「a modified version of」が追加されています。ただこれは、.NET Framework 3までのMSDNに間違があったということのようです。
Randomクラスを使用して生成した乱数は、予測が比較的容易です。よって、厳密な乱数が必要なケースで使用するのは危険です。
以下にRandomクラスを使って乱数を生成するいくつかの例を示します。
'シード値(1000)を使用して初期化 'シード値が変わらなければ毎回同じ乱数を返す Dim r As New System.Random(1000) 'シード値を指定しないとシード値として Environment.TickCount が使用される 'Dim r As New System.Random() '0以上10未満の乱数を整数で返す Dim i1 As Integer = r.Next(10) '-10以上10未満の乱数を整数で返す Dim i2 As Integer = r.Next(-10, 10) '0以上Int32.MaxValue未満の乱数を整数で返す Dim i3 As Integer = r.Next() 'バイト配列の要素に乱数を入れる Dim bs As Byte() = New Byte(7) {} r.NextBytes(bs) '0.0以上1.0未満の乱数を倍精度浮動小数点数で返す Dim d As Double = r.NextDouble()
//シード値(1000)を使用して初期化 //シード値が変わらなければ毎回同じ乱数を返す System.Random r = new System.Random(1000); //シード値を指定しないとシード値として Environment.TickCount が使用される //System.Random r = new System.Random(); //0以上10未満の乱数を整数で返す int i1 = r.Next(10); //-10以上10未満の乱数を整数で返す int i2 = r.Next(-10, 10); //0以上Int32.MaxValue未満の乱数を整数で返す int i3 = r.Next(); //バイト配列の要素に乱数を入れる byte[] bs = new byte[8]; r.NextBytes(bs); //0.0以上1.0未満の乱数を倍精度浮動小数点数で返す Double d = r.NextDouble();
補足:.NET Framework 2.0以降では、NextDoubleメソッドは、Next()メソッド(パラメータなし)に 4.6566128752457969E-10 を掛けた値を返します(正確にはパタメータのないNextメソッドではなく、privateメソッドのInternalSampleメソッドです)。パラメータを1つ持つNextメソッドは、NextDoubleメソッドの結果を使って計算されています(正確にはNextDoubleメソッドではなく、protectedメソッドのSampleメソッドです)。パラメータを2つ持つNextメソッドは、Next()またはNextDoubleメソッドの結果を使って計算されています。NextBytesメソッドは、Next()メソッドの結果を使って計算されています。.NET 1.1以前では、NextDoubleメソッドの結果を使ってすべてのNextメソッドとNextBytesメソッドが計算されています。
暗号化に使用する厳密なランダムバイトを作成するには、RNGCryptoServiceProviderクラスのGetBytesメソッドを使用します。
この方法はRandomクラスを使用した方法と比べ、かなり遅いです。よって、ケースバイケースで使い分けてください。
'暗号化に使用する厳密なランダムバイトを作成する '100バイト長のバイト型配列を作成 Dim random() As Byte = New Byte(100) {} 'RNGCryptoServiceProviderクラスのインスタンスを作成 Dim rng As New System.Security.Cryptography.RNGCryptoServiceProvider() 'または、次のようにもできる 'Dim rng As System.Security.Cryptography.RandomNumberGenerator = _ ' System.Security.Cryptography.RandomNumberGenerator.Create() 'バイト配列に暗号化に使用する厳密な値のランダムシーケンスを設定 rng.GetBytes(random) 'バイト配列に暗号化に使用する厳密な0以外の値の 'ランダムシーケンスを設定するには次のようにする 'rng.GetNonZeroBytes(random) '後始末(.NET Framework 4.0以降) rng.Dispose()
//暗号化に使用する厳密なランダムバイトを作成する //100バイト長のバイト型配列を作成 byte[] random = new byte[100]; //RNGCryptoServiceProviderクラスのインスタンスを作成 System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); //または、次のようにもできる //System.Security.Cryptography.RandomNumberGenerator rng = // System.Security.Cryptography.RandomNumberGenerator.Create(); //バイト配列に暗号化に使用する厳密な値のランダムシーケンスを設定 rng.GetBytes(random); //バイト配列に暗号化に使用する厳密な0以外の値の //ランダムシーケンスを設定するには次のようにする //rng.GetNonZeroBytes(random); //後始末(.NET Framework 4.0以降) rng.Dispose();
RNGCryptoServiceProviderクラスのGetBytesメソッドではByte型の配列を取得できますが、例えばInt32型やInt64型のランダムな数値で取得するには、Byte型配列をBitConverterクラスで変換します。
Int32型のランダムな値を取得する例を以下に示します。ランダムな値は、Int32.MinValue以上Int32.MaxValue以下のいずれかの値になります。
'Int32と同じサイズのバイト配列にランダムな値を設定する Dim bs As Byte() = New Byte(3) {} Dim rng As New System.Security.Cryptography.RNGCryptoServiceProvider() rng.GetBytes(bs) '.NET Framework 4.0以降 rng.Dispose() 'Int32に変換する Dim i As Integer = System.BitConverter.ToInt32(bs, 0)
//Int32と同じサイズのバイト配列にランダムな値を設定する //byte[] bs = new byte[sizeof(int)]; byte[] bs = new byte[4]; System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); rng.GetBytes(bs); //.NET Framework 4.0以降 rng.Dispose(); //Int32に変換する int i = System.BitConverter.ToInt32(bs, 0);
RNGCryptoServiceProviderクラスほどの厳密さは必要ないが、Randomクラスでは不十分という場合は、別の乱数生成アルゴリズムを使用する方法も考えられます。その方法は.NET Frameworkには用意されていませんので、自分でコードを書くか、サードパーティーのライブラリを使用することになります。
例えば、「Math.NET Numerics」のMathNet.Numerics.Random名前空間には、Mersenne Twister(メルセンヌ・ツイスタ)やXorshiftなど、様々な乱数生成アルゴリズムに対応したクラスが用意されています。これらのクラスは、.NET FrameworkのRandomクラスを継承していますので、Randomクラスと同じように使用することができます。
Xorshift(xor128)により乱数を生成する例を示します。この例では、Randomクラスの派生クラスを作成しているためコードは長くなっていますが、実際に乱数を生成しているのはNextUInt32メソッドの部分のみです。
Imports System ''' <summary> ''' "Xorshift RNGs" (George Marsaglia) の xor128 アルゴリズムを使用した ''' 擬似乱数ジェネレーターを表します。 ''' </summary> ''' <remarks> ''' "Xorshift RNGs" ''' http://www.jstatsoft.org/v08/i14/paper ''' </remarks> Public Class Xorshift128 Inherits Random Private _x As UInteger Private _y As UInteger Private _z As UInteger Private _w As UInteger ''' <summary> ''' 指定した4つのシード値を使用して、 ''' Xorshift128クラスのインスタンスを初期化します。 ''' </summary> ''' <param name="seedX">シード値の1つ (x)。</param> ''' <param name="seedY">シード値の1つ (y)。</param> ''' <param name="seedZ">シード値の1つ (z)。</param> ''' <param name="seedW">シード値の1つ (w)。</param> Public Sub New(ByVal seedX As UInteger, _ ByVal seedY As UInteger, _ ByVal seedZ As UInteger, _ ByVal seedW As UInteger) 'すべて0であってはならない If seedX = 0 AndAlso seedY = 0 AndAlso seedZ = 0 AndAlso seedW = 0 Then Throw New ArgumentException() End If _x = seedX _y = seedY _z = seedZ _w = seedW End Sub 'seedに負数を指定した場合の挙動は、Randomクラスとは異なる Public Sub New(ByVal seed As Integer) Me.New(CUInt((seed And 2147483647)), 362436069, 521288629, 88675123) End Sub Public Sub New() Me.New(Environment.TickCount) End Sub ''' <summary> ''' 乱数を返します。 ''' </summary> ''' <returns> ''' UInt32.MinValue 以上 UInt32.MaxValue 以下の ''' 32ビット符号なし整数。 ''' </returns> Public Function NextUInt32() As UInteger '実際に乱数を生成している部分 Dim t As UInteger = _x Xor (_x << 11) _x = _y _y = _z _z = _w _w = (_w Xor (_w >> 19)) Xor (t Xor (t >> 8)) Return _w End Function Protected Overloads Overrides Function Sample() As Double 'UInt32の値を、0.0以上1.0未満のDouble値に変換する Return NextUInt32() * (1.0 / (UInteger.MaxValue + 1.0)) End Function Public Overloads Overrides Function NextDouble() As Double Return Sample() End Function Public Overloads Overrides Function [Next](ByVal maxValue As Integer) _ As Integer If maxValue < 0 Then Throw New ArgumentOutOfRangeException("maxValue") End If '偏りのある方法だが、簡易的にSample()を使用して計算する '偏りのない方法は、Math.NET NumericsのRandomSource.csを参照 Return Math.Truncate((Sample() * maxValue)) End Function Public Overloads Overrides Function [Next](ByVal minValue As Integer, _ ByVal maxValue As Integer) _ As Integer If maxValue < minValue Then Throw New ArgumentOutOfRangeException("minValue") End If Return Math.Truncate( (Sample() * (CDbl(maxValue) - minValue) + minValue)) End Function Public Overloads Overrides Function [Next]() As Integer Return Math.Truncate((Sample() * Integer.MaxValue)) End Function Public Overloads Overrides Sub NextBytes(ByVal buffer As Byte()) If buffer Is Nothing Then Throw New ArgumentNullException("buffer") End If Dim len As Integer = buffer.Length For i As Integer = 0 To len - 1 buffer(i) = CByte((NextUInt32() Mod 256)) Next End Sub End Class
using System; /// <summary> /// "Xorshift RNGs" (George Marsaglia) の xor128 アルゴリズムを使用した /// 擬似乱数ジェネレーターを表します。 /// </summary> /// <remarks> /// "Xorshift RNGs" /// http://www.jstatsoft.org/v08/i14/paper /// </remarks> public class Xorshift128 : Random { private uint _x; private uint _y; private uint _z; private uint _w; /// <summary> /// 指定した4つのシード値を使用して、 /// Xorshift128クラスのインスタンスを初期化します。 /// </summary> /// <param name="seedX">シード値の1つ (x)。</param> /// <param name="seedY">シード値の1つ (y)。</param> /// <param name="seedZ">シード値の1つ (z)。</param> /// <param name="seedW">シード値の1つ (w)。</param> public Xorshift128(uint seedX, uint seedY, uint seedZ, uint seedW) { //すべて0であってはならない if (seedX == 0u && seedY == 0u && seedZ == 0u && seedW == 0u) { throw new ArgumentException(); } _x = seedX; _y = seedY; _z = seedZ; _w = seedW; } //seedに負数を指定した場合の挙動は、Randomクラスとは異なる public Xorshift128(int seed) : this((uint)(seed & 0x7FFFFFFF), 362436069u, 521288629u, 88675123u) { } public Xorshift128() : this(Environment.TickCount) { } /// <summary> /// 乱数を返します。 /// </summary> /// <returns> /// UInt32.MinValue 以上 UInt32.MaxValue 以下の /// 32ビット符号なし整数。 /// </returns> public uint NextUInt32() { //実際に乱数を生成している部分 uint t = _x ^ (_x << 11); _x = _y; _y = _z; _z = _w; return (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))); } protected override double Sample() { //UInt32の値を、0.0以上1.0未満のDouble値に変換する return NextUInt32() * (1.0 / (uint.MaxValue + 1.0)); } public override double NextDouble() { return Sample(); } public override int Next(int maxValue) { if (maxValue < 0) { throw new ArgumentOutOfRangeException("maxValue"); } //偏りのある方法だが、簡易的にSample()を使用して計算する //偏りのない方法は、Math.NET NumericsのRandomSource.csを参照 return (int)(Sample() * maxValue); } public override int Next(int minValue, int maxValue) { if (maxValue < minValue) { throw new ArgumentOutOfRangeException("minValue"); } return (int)(Sample() * ((double)maxValue - minValue) + minValue); } public override int Next() { return (int)(Sample() * int.MaxValue); } public override void NextBytes(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } int len = buffer.Length; for (int i = 0; i < len; i++) { buffer[i] = (byte)(NextUInt32() % 256); } } }