DOBON.NET

乱数を生成する

乱数を生成するには、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クラスを使って乱数を生成するいくつかの例を示します。

VB.NET
コードを隠すコードを選択
'シード値(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()
C#
コードを隠すコードを選択
//シード値(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クラスを使用した方法と比べ、かなり遅いです。よって、ケースバイケースで使い分けてください。

VB.NET
コードを隠すコードを選択
'暗号化に使用する厳密なランダムバイトを作成する
'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()
C#
コードを隠すコードを選択
//暗号化に使用する厳密なランダムバイトを作成する
//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クラスでランダムな数値を取得する

RNGCryptoServiceProviderクラスのGetBytesメソッドではByte型の配列を取得できますが、例えばInt32型やInt64型のランダムな数値で取得するには、Byte型配列をBitConverterクラスで変換します。

Int32型のランダムな値を取得する例を以下に示します。ランダムな値は、Int32.MinValue以上Int32.MaxValue以下のいずれかの値になります。

VB.NET
コードを隠すコードを選択
'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)
C#
コードを隠すコードを選択
//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);

Mersenne TwisterやXorshiftなどで乱数を生成する

RNGCryptoServiceProviderクラスほどの厳密さは必要ないが、Randomクラスでは不十分という場合は、別の乱数生成アルゴリズムを使用する方法も考えられます。その方法は.NET Frameworkには用意されていませんので、自分でコードを書くか、サードパーティーのライブラリを使用することになります。

例えば、「Math.NET Numerics」のMathNet.Numerics.Random名前空間には、Mersenne Twister(メルセンヌ・ツイスタ)やXorshiftなど、様々な乱数生成アルゴリズムに対応したクラスが用意されています。これらのクラスは、.NET FrameworkのRandomクラスを継承していますので、Randomクラスと同じように使用することができます。

Xorshiftを使用した例

Xorshift(xor128)により乱数を生成する例を示します。この例では、Randomクラスの派生クラスを作成しているためコードは長くなっていますが、実際に乱数を生成しているのはNextUInt32メソッドの部分のみです。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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);
        }
    }
}
  • 履歴:
  • 2009/9/9 説明を追加。
  • 2010/7/22 「RNGCryptoServiceProviderクラスでランダムな数値を取得する」を追加。Randomクラスに関する補足を追加。
  • 2018/2/19 「Mersenne TwisterやXorshiftなどで乱数を生成する」を追加。MSDNに「a modified version of」が追加された件を補足で追加。RNGCryptoServiceProvider.Disposeを追加。「RNGCryptoServiceProviderクラスでランダムな数値を取得する」の補足を削除。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • 「???を参照に追加します」の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。
共有する

この記事への評価

この記事へのコメント

この記事に関するコメントを投稿するには、下のボタンをクリックしてください。投稿フォームへ移動します。通常のご質問、ご意見等は掲示板へご投稿ください。