乱数を生成する方法として、.NETでは疑似乱数を生成するRandomクラスが用意されています。

このほかに、疑似乱数ではなく、生成される結果が予測不可能な乱数列を必要とする目的には、暗号乱数ジェネレーター(暗号論的擬似乱数生成器)としてRandomNumberGeneratorクラスを使用することもできます。 ここでは、Randomクラス・RandomNumberGeneratorクラスを使って乱数を生成する方法について解説します。

なお、以下の解説およびサンプルコードは、乱数の特徴や分布・予測不可能性などを十分に考慮したものではなく、乱数の生成を主眼においたものとなっています。 そのため、暗号・確率等の分野へ適用するには不適当な可能性があるため、必要に応じて他のドキュメントを参照するなどしてより適切な実装を行うようにしてください。

Randomクラス

乱数を得るには、Randomクラスを使用します。 RandomクラスはMathクラスとは異なり、静的メソッドを呼び出して使用するクラスではないため、インスタンスを作成してから使用する必要があります。

Randomクラスのインスタンスを作成して乱数を生成する
using System;

class Sample {
  static void Main()
  {
    // Randomクラスのインスタンスを作成
    var rand = new Random();

    // 0以上100未満の乱数を20個生成して表示する
    for (var i = 0; i < 20; i++) {
      Console.Write("{0}, ", rand.Next(0, 100));
    }

    Console.WriteLine();
  }
}
Randomクラスのインスタンスを作成して乱数を生成する
Imports System

Class Sample
  Shared Sub Main()
    ' Randomクラスのインスタンスを作成
    Dim rand As New Random()

    ' 0以上100未満の乱数を20個生成して表示する
    For i As Integer = 1 To 20
      Console.Write("{0}, ", rand.Next(0, 100))
    Next

    Console.WriteLine()
  End Sub
End Class
実行結果の一例
76, 38, 93, 98, 31, 42, 31, 70, 91, 40, 74, 80, 43, 67, 25, 31, 23, 83, 72, 49, 

Randomクラスは疑似乱数を生成します。 必要に応じて、コンストラクタで乱数系列のシード値を指定することもできます。

乱数の生成 (Next/NextDouble)

Randomクラスには、乱数を得るためのメソッドがいくつか用意されています。 求める乱数の型・値域によって、次のメソッドを使い分けることができます。 いずれも得られる乱数の値域は半開区間(n以上m未満)となっています。

NextDoubleメソッド
0.0以上1.0未満の乱数を実数(Double)で取得する。 [0.0, 1.0)
Nextメソッド
乱数を整数(Int32)で取得する。 以下のオーバーロードが用意されている。
Next()
0以上の乱数を取得する。 [0, Int32.MaxValue)
Next(max)
0以上max未満の乱数を取得する。 [0, max)
Next(min, max)
min以上max未満の乱数を取得する。 [min, max)

なお、NextDoubleメソッドには任意の値域を指定するバージョンは用意されていないため、NextDoubleの戻り値を加減乗除して求める値域に変換する必要があります。

Random.Nextメソッドで整数の乱数を生成する
using System;

class Sample {
  static void Main()
  {
    var rand = new Random();

    for (var i = 0; i < 20; i++) {
      // 1以上6以下(7未満)の乱数を生成する
      Console.Write("{0}, ", rand.Next(1, 7));
    }
    Console.WriteLine();
  }
}
Random.Nextメソッドで整数の乱数を生成する
Imports System

Class Sample
  Shared Sub Main()
    Dim rand As New Random()

    For i As Integer = 1 To 20
      ' 1以上6以下(7未満)の乱数
      Console.Write("{0}, ", rand.Next(1, 7))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果の一例
6, 3, 6, 6, 1, 6, 6, 5, 3, 4, 5, 2, 5, 1, 3, 5, 1, 2, 2, 6, 
Random.NextDoubleメソッドで実数の乱数を生成する
using System;

class Sample {
  static void Main()
  {
    var rand = new Random();

    for (var i = 0; i < 20; i++) {
      // 0以上1未満の乱数を生成したのち、値域を0以上2未満に変換する
      Console.Write("{0:F1}, ", 2.0 * rand.NextDouble());
    }
    Console.WriteLine();

    for (var i = 0; i < 20; i++) {
      // 0以上1未満の乱数を生成したのち、値域を-1以上+1未満に変換する
      Console.Write("{0:F1}, ", 2.0 * rand.NextDouble() - 1.0);
    }
    Console.WriteLine();
  }
}
Random.NextDoubleメソッドで実数の乱数を生成する
Imports System

Class Sample
  Shared Sub Main()
    Dim rand As New Random()

    For i As Integer = 1 To 20
      ' 0以上1未満の乱数を生成したのち、値域を0以上2未満に変換する
      Console.Write("{0:F1}, ", 2.0 * rand.NextDouble())
    Next
    Console.WriteLine()

    For i As Integer = 1 To 20
      ' 0以上1未満の乱数を生成したのち、値域を-1以上+1未満に変換する
      Console.Write("{0:F1}, ", 2.0 * rand.NextDouble() - 1.0)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果の一例
1.8, 2.0, 1.7, 1.5, 0.3, 0.3, 0.6, 0.0, 0.1, 1.3, 1.7, 0.1, 1.1, 0.5, 2.0, 0.2, 1.1, 1.1, 1.7, 1.4, 
0.3, -0.2, 0.7, -0.9, -0.8, -0.1, 0.8, -0.3, -0.0, -0.3, 0.6, -0.8, 0.5, 0.5, 0.3, 0.7, -0.3, 0.9, 0.8, -0.1,

バイト配列を乱数で満たす (NextBytes)

NextBytesメソッドを使うと、バイト配列を0からByte.MaxValueまでのランダムな値で満たすことができます。 nonce値を生成したい場合などに、このメソッドを使うことができます。

Random.NextBytesメソッドでバイト配列を乱数で満たす
using System;

class Sample {
  static void Main()
  {
    var nonce = new byte[16];
    var rand = new Random();

    // NextBytesを使って配列をランダムな値で満たす(16バイトのnonce値を生成する)
    rand.NextBytes(nonce);

    // 生成したnonceを表示
    Console.WriteLine(BitConverter.ToString(nonce));
    // 生成したnonceをBASE64形式で表示
    Console.WriteLine(Convert.ToBase64String(nonce));
  }
}
Random.NextBytesメソッドでバイト配列を乱数で満たす
Imports System

Class Sample
  Shared Sub Main()
    Dim nonce(15) As Byte
    Dim rand As New Random()

    ' NextBytesを使って配列をランダムな値で満たす(16バイトのnonce値を生成する)
    rand.NextBytes(nonce)

    ' 生成したnonceを表示
    Console.WriteLine(BitConverter.ToString(nonce))
    ' 生成したnonceをBASE64形式で表示
    Console.WriteLine(Convert.ToBase64String(nonce))
  End Sub
End Class
実行結果の一例
5D-DA-A5-7C-17-F5-12-FD-28-12-77-27-8C-41-33-D1
XdqlfBf1Ev0oEncnjEEz0Q==

実際にnonce値として使用する乱数の場合は、RandomNumberGeneratorクラスを使ったほうが適当な場合が多いと思われます。 RandomNumberGeneratorクラスを使ってnonce値を生成する具体例は§.乱数の生成 (GetBytes)をご覧ください。


.NET Standard 2.1/.NET Core 2.1以降では、バイト配列(byte[])だけでなく、Span<byte>を引数にとるバージョンも追加されています。 これにより、バイト配列の一部分だけを乱数で埋めたり、配列以外のバイト列を乱数で埋めるといったことができます。

Random.NextBytesメソッドでバイト配列の一部分のみを乱数で満たす .NET Standard 2.1/.NET Core 2.1
using System;

class Sample {
  static void Main()
  {
    var nonce = new byte[16];
    var rand = new Random();

    // NextBytesを使って配列の先頭8バイトのみをランダムな値で満たす
    rand.NextBytes(nonce.AsSpan(0, 8));

    // 生成したnonceを表示
    Console.WriteLine(BitConverter.ToString(nonce));
    // 生成したnonceをBASE64形式で表示
    Console.WriteLine(Convert.ToBase64String(nonce));
  }
}
Random.NextBytesメソッドでバイト配列の一部分のみを乱数で満たす .NET Standard 2.1/.NET Core 2.1
Imports System

Class Sample
  Shared Sub Main()
    Dim nonce(15) As Byte
    Dim rand As New Random()

    ' NextBytesを使って配列の先頭8バイトのみをランダムな値で満たす
    rand.NextBytes(nonce.AsSpan(0, 8))

    ' 生成したnonceを表示
    Console.WriteLine(BitConverter.ToString(nonce))
    ' 生成したnonceをBASE64形式で表示
    Console.WriteLine(Convert.ToBase64String(nonce))
  End Sub
End Class
実行結果の一例
06-4A-5F-76-CA-12-7E-CA-00-00-00-00-00-00-00-00
BkpfdsoSfsoAAAAAAAAAAA==
Random.NextBytesメソッドでSpan<byte>を乱数で満たす .NET Standard 2.1/.NET Core 2.1
using System;

class Sample {
  static void Main()
  {
    Span<byte> nonce = stackalloc byte[16];
    var rand = new Random();

    // NextBytesを使ってstackallocで確保したメモリ領域をランダムな値で満たす
    rand.NextBytes(nonce);

    // 生成したnonceを表示
    //Console.WriteLine(Convert.ToHexString(nonce)); // .NET 5
    // 生成したnonceをBASE64形式で表示
    Console.WriteLine(Convert.ToBase64String(nonce));
  }
}
実行結果の一例
GA762jYsr5W3VLW/Pxhj7g==

乱数のシード

Randomクラスでは、一つのインスタンスが一つの乱数系列として動作します。 またRandomクラスでは、個々のインスタンスに異なるシード値(seed, 乱数の)を与えることにより、複数の乱数系列を生成することができます。

Randomクラスのコンストラクタでは、乱数系列のシード値を指定することができます。 このとき、同一のシード値を与えた場合は同一の乱数系列となり、同じ乱数を生成します。 これにより、インスタンスを生成したときのシード値を保存しておくことで、そのときと同じ乱数を生成するという使い方もできます。

シード値を指定してRandomクラスのインスタンスを作成する
using System;

class Sample {
  static void Main()
  {
    var rand1 = new Random(0); // シード値(0)を与えてインスタンスを作成する
    var rand2 = new Random(1);
    var rand3 = new Random(1); // rand2と同じシード値のインスタンスを作成する

    const string format = "{0,-6} {1,-6} {2,-6}";

    Console.WriteLine(
      format,
      nameof(rand1),
      nameof(rand2),
      nameof(rand3)
    );

    // 各インスタンスから乱数を生成して表示する
    for (var i = 0; i < 15; i++) {
      Console.WriteLine(
        format,
        rand1.Next(0, 100),
        rand2.Next(0, 100),
        rand3.Next(0, 100)
      );
    }
  }
}
シード値を指定してRandomクラスのインスタンスを作成する
Imports System

Class Sample
  Shared Sub Main()
    Dim rand1 As New Random(0) ' シード値(0)を与えてインスタンスを作成する
    Dim rand2 As New Random(1)
    Dim rand3 As New Random(1) ' rand2と同じシード値のインスタンスを作成する

    Const format As String = "{0,-6} {1,-6} {2,-6}"

    Console.WriteLine(
      format,
      NameOf(rand1),
      NameOf(rand2),
      NameOf(rand3)
    )

    ' 各インスタンスから乱数を生成して表示する
    For i As Integer = 1 To 15
      Console.WriteLine(
        format,
        rand1.Next(0, 100),
        rand2.Next(0, 100),
        rand3.Next(0, 100)
      )
    Next
  End Sub
End Class
実行結果
rand1  rand2  rand3 
72     24     24    
81     11     11    
76     46     46    
55     77     77    
20     65     65    
55     43     43    
90     35     35    
44     94     94    
97     10     10    
27     64     64    
29     2      2     
46     24     24    
63     32     32    
46     98     98    
98     68     68    

.NET Frameworkにおいて、Randomクラスのコンストラクタでシード値を指定しなかった場合は、デフォルト値としてシステム時計の値(インスタンスを生成するときのEnvironment.TickCountの値)が使用されます。 このため、実行するタイミングによって毎回異なるシード値のインスタンスが作成されることになります。 ただし、Environment.TickCountの値は短時間に連続して取得すると同じ値が返される場合があります。 これにより、シード値を指定せず、短時間で複数のRandomクラスのインスタンスを連続して生成すると、同一のシード値からなるインスタンス(つまり同一の乱数系列)が生成される可能性がある点に注意する必要があります。

.NET Core/.NET 5以降では、デフォルトのシード値自体も別の乱数系列から取得されるため、インスタンスごとに毎回異なるシード値が与えられます。

短時間に連続してRandomインスタンスを作成する場合のシード値
using System;
using System.Threading;

class Sample {
  static void Main()
  {
    // シード値を指定せずにインスタンスを連続して生成する
    // ・.NET Frameworkでは、デフォルトのシード値としてEnvironment.TickCountの値が用いられるため
    //  連続して作成したインスタンスが同一のシード値を持つ場合がある
    // ・.NET Core/.NET 5では、Environment.TickCountではなく呼び出し毎に異なるシード値を用いるため、
    //  連続して作成した場合でもインスタンスが同一のシード値を持つことはない
    //  (RandomNumberGenerator相当から取得されるシード値を元にグローバルなRandomが作成され、
    //  そこから得られる乱数がシード値として用いられる)
    var rand1 = new Random();
    var rand2 = new Random();

    Thread.Sleep(100); // 100ミリ秒待機

    // シード値を指定せずにインスタンスを生成
    var rand3 = new Random();

    const string format = "{0,-6} {1,-6} {2,-6}";

    Console.WriteLine(
      format,
      nameof(rand1),
      nameof(rand2),
      nameof(rand3)
    );

    // 各インスタンスから乱数を生成して表示する
    for (var i = 0; i < 15; i++) {
      Console.WriteLine(
        format,
        rand1.Next(0, 100),
        rand2.Next(0, 100),
        rand3.Next(0, 100)
      );
    }
  }
}
短時間に連続してRandomインスタンスを作成する場合のシード値
Imports System
Imports System.Threading

Class Sample
  Shared Sub Main()
    ' シード値を指定せずにインスタンスを連続して生成する
    ' ・.NET Frameworkでは、デフォルトのシード値としてEnvironment.TickCountの値が用いられるため
    '  連続して作成したインスタンスが同一のシード値を持つ場合がある
    ' ・.NET Core/.NET 5では、Environment.TickCountではなく呼び出し毎に異なるシード値を用いるため、
    '  連続して作成した場合でもインスタンスが同一のシード値を持つことはない
    '  (RandomNumberGenerator相当から取得されるシード値を元にグローバルなRandomが作成され、
    '  そこから得られる乱数がシード値として用いられる)
    Dim rand1 As New Random()
    Dim rand2 As New Random()

    Thread.Sleep(100) ' 100ミリ秒待機

    ' シード値を指定せずにインスタンスを生成
    Dim rand3 As New Random()

    Const format As String = "{0,-6} {1,-6} {2,-6}"

    Console.WriteLine(
      format,
      NameOf(rand1),
      NameOf(rand2),
      NameOf(rand3)
    )

    ' 各インスタンスから乱数を生成して表示する
    For i As Integer = 1 To 15
      Console.WriteLine(
        format,
        rand1.Next(0, 100),
        rand2.Next(0, 100),
        rand3.Next(0, 100)
      )
    Next
  End Sub
End Class
.NET Frameworkでの実行結果例
rand1 rand2 rand3
51    51    55   
78    78    55   
88    88    36   
62    62    2    
18    18    36   
6     6     37   
35    35    91   
25    25    13   
66    66    67   
47    47    28   
42    42    1    
23    23    79   
4     4     53   
4     4     14   
48    48    70   
.NET Coreでの実行結果例
rand1  rand2  rand3 
94     68     94    
89     88     31    
28     3      74    
97     15     88    
36     73     44    
57     41     52    
12     69     71    
37     79     41    
27     14     16    
89     67     18    
52     98     39    
81     51     38    
0      18     17    
48     90     11    
18     80     61    

乱数アルゴリズムの実装

擬似乱数の生成アルゴリズムにはいくつか種類がありますが、Randomクラスを継承して独自に疑似乱数を実装することもできます。 Randomクラスを継承して疑似乱数生成アルゴリズムを実装する際に最低限必要となるのは、Sampleメソッドをオーバーライドし、0.0以上1.0未満の乱数を返すように実装することです。

以下の例では、線形合同法を実装したLCGRandomクラスを作成し、Randomクラスが生成する乱数との比較を行っています。 なお、この実装は一つの例として挙げたもので、生成される乱数の周期や分散については考慮していません。 実際に使用する場合は注意してください。

Randomクラスを継承して線形合同法による擬似乱数を実装する
using System;

// 線形合同法による疑似乱数の生成を実装したクラス
class LCGRandom : Random {
  private long x;
  private const long a = 1140671485;
  private const long c = 12820163;

  public LCGRandom()
    : this(Environment.TickCount) // Environment.TickCountの値をデフォルトのシード値として使用する
  {
  }

  public LCGRandom(int seed)
  {
    x = seed;
  }

  protected override double Sample()
  {
    x = unchecked(a * x + c) & long.MaxValue;

    return (double)x / long.MaxValue;
  }
}

class Sample {
  static void Main()
  {
    var rand = new Random(0);
    var lcg  = new LCGRandom(0);

    const string format = "{0,-6} {1,-6}";

    Console.WriteLine(
      format,
      nameof(rand),
      nameof(lcg)
    );

    for (var i = 0; i < 15; i++) {
      Console.WriteLine(
        format,
        rand.Next(0, 100),
        lcg.Next(0, 100)
      );
    }
  }
}

以下のコードでは、Sampleメソッドでオーバーフローが発生するため、/removeintchecks+オプションを有効にしてコンパイルするか、プロジェクトファイルにてRemoveIntegerChecksTrueにしてコンパイルしないと例外OverflowExceptionがスローされます。

Randomクラスを継承して線形合同法による擬似乱数を実装する
' vbc /removeintchecks+
' dotnet build -p:RemoveIntegerChecks=True
Imports System

' 線形合同法による疑似乱数の生成を実装したクラス
Class LCGRandom
  Inherits Random

  Private x As Long
  Private Const a As Long = 1140671485
  Private Const c As Long = 12820163

  Public Sub New()
    Me.New(Environment.TickCount) ' Environment.TickCountの値をデフォルトのシード値として使用する
  End Sub

  Public Sub New(ByVal seed As Integer)
    x = seed
  End Sub

  Protected Overrides Function Sample() As Double
    x = (a * x + c) And Long.MaxValue

    Return CDbl(x) / Long.MaxValue
  End Function
End Class

Class Sample
  Shared Sub Main()
    Dim rand As New Random(0)
    Dim lcg As New LCGRandom(0)

    Const format As String = "{0,-6} {1,-6}"

    Console.WriteLine(
      format,
      NameOf(rand),
      NameOf(lcg)
    )

    For i As Integer = 1 To 15
      Console.WriteLine(
        format,
        rand.Next(0, 100),
        lcg.Next(0, 100)
      )
    Next
  End Sub
End Class
実行結果
rand   lcg   
72     0     
81     0     
76     97    
55     2     
20     65    
55     93    
90     23    
44     65    
97     44    
27     61    
29     37    
46     18    
63     78    
46     82    
98     28    

この例ではオーバーフロー時に例外のスローを抑止する目的でuncheckedステートメント、および/removeintchecks+コンパイルオプションを使用しています。 詳しくは整数型のオーバーフローとチェックを参照してください。

このように、Sampleメソッドをオーバーライドすることで独自の乱数生成を行うことができます。 場合によっては、Sampleメソッドを適切に実装することでメソッドの戻り値の範囲が閉区間となるようにすることもできます。

なお、Randomクラスを継承する場合、引数をとらないNextメソッドなどSample以外のメソッドもオーバーライドしないと動作が変わらないメソッドがあります。 詳しくはSampleメソッドのリファレンスを参照してください。

RandomNumberGeneratorクラス

Randomクラスが生成する乱数は疑似乱数であり、既知の乱数生成アルゴリズムに基づいて生成されます。 そのため、Randomクラスでは次に生成される乱数を予測することは不可能ではありません。 このような特徴は、特に暗号分野などにおいて、乱数源としては不適当な場合があります。

次の乱数を予測しうるRandomクラス・疑似乱数に対し、予測できない/しにくい乱数を生成する乱数源として、RandomNumberGeneratorクラスを用いることができます。

インスタンスの作成と破棄

Randomクラスと同様、RandomNumberGeneratorクラスで乱数を生成するためにはまずインスタンスを作成する必要があります。 RandomNumberGeneratorは抽象クラスであるため、インスタンスはnewによる作成ではなく、Createメソッドを使って作成します。

.NET Framework 4.0以降のRandomNumberGeneratorはIDisposableインターフェイスを実装しています。 そのため、usingステートメントを使って次のようにインスタンスの作成・破棄を行います。

RandomNumberGeneratorインスタンスの作成と破棄 
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    // RandomNumberGenerator.Createメソッドでインスタンスを作成する
    // (usingステートメントを使うことで、スコープから抜けた時点で自動的にDisposeさせる)
    using var rng = RandomNumberGenerator.Create();

    // 以下、インスタンスを使用するコードを記述する
    var nonce = new byte[16];

    rng.GetBytes(nonce);

    // スコープから抜けた時点で、インスタンスが自動的にDisposeされる
  }
}
RandomNumberGeneratorインスタンスの作成と破棄
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    // RandomNumberGenerator.Createメソッドでインスタンスを作成する
    using (var rng = RandomNumberGenerator.Create()) {
      // 以下、インスタンスを使用するコードを記述する
      var nonce = new byte[16];

      rng.GetBytes(nonce);
    } // usingステートメントから抜けた時点で、インスタンスが自動的にDisposeされる
  }
}
RandomNumberGeneratorインスタンスの作成と破棄
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    ' RandomNumberGenerator.Createメソッドでインスタンスを作成する
    Using rng As RandomNumberGenerator = RandomNumberGenerator.Create()
      ' 以下、インスタンスを使用するコードを記述する
      Dim nonce(15) As Byte

      rng.GetBytes(nonce)
    End Using
    ' Usingステートメントから抜けた時点で、インスタンスが自動的にDisposeされる
  End Sub
End Class

RandomNumberGenerator.Createメソッドに引数(乱数ジェネレータの名前)を指定せずに作成したインスタンスの場合はデフォルトの乱数ジェネレータが生成されます。

デフォルトのジェネレータに対してはDisposeメソッドを呼び出す必要はないようですが、乱数ジェネレータの種類によってはアンマネージリソースの解放が必要な実装となっている可能性があるため、usingステートメントによって必ずDisposeメソッドが呼び出されるようにしておくのが無難です。

usingステートメントとインスタンスの破棄についてはオブジェクトの破棄 §.usingステートメントを参照してください。

乱数の生成 (GetBytes)

RandomNumberGeneratorクラスを使って乱数を生成するには、GetBytesメソッドを使います。 GetBytesメソッドは、Random.NextBytesメソッドと同様のメソッドで、引数として与えられたバイト配列を乱数で満たします。

RandomNumberGenerator.GetBytesメソッドでバイト配列を乱数で満たす
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    using var rng = RandomNumberGenerator.Create();

    // RandomNumberGeneratorを使って16バイトのnonce値を3個生成する
    var nonce = new byte[16];

    for (var i = 0; i < 3; i++) {
      rng.GetBytes(nonce);

      Console.WriteLine(BitConverter.ToString(nonce));
    }
  }
}
RandomNumberGenerator.GetBytesメソッドでバイト配列を乱数で満たす
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    Using rng As RandomNumberGenerator = RandomNumberGenerator.Create()
      ' RandomNumberGeneratorを使って16バイトのnonce値を3個生成する
      Dim nonce(15) As Byte

      For i As Integer = 1 To 3
        rng.GetBytes(nonce)

        Console.WriteLine(BitConverter.ToString(nonce))
      Next
    End Using
  End Sub
End Class
実行結果の一例
44-55-FB-46-91-80-CB-8E-EC-52-5A-06-48-13-46-B5
F7-30-40-46-6E-1B-80-00-B5-61-D5-13-0D-3A-BD-9E
5C-A6-2D-70-FF-55-BA-BF-E5-A1-D1-F8-94-1D-55-A6

Randomクラスとは異なり、RandomNumberGeneratorはシード値を必要としません。 RandomNumberGeneratorでは、インスタンスに関わらずメソッド呼び出し毎に異なる乱数を得ることができます。

非ゼロ乱数の生成 (GetNonZeroBytes)

生成される乱数に0が含まれないようにしたい場合は、GetNonZeroBytesメソッドを使うことができます。

RandomNumberGenerator.GetNonZeroBytesメソッドでバイト配列を非ゼロの乱数で満たす
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    using var rng = RandomNumberGenerator.Create();

    // RandomNumberGeneratorを使って0を含まない、16バイトのnonce値を3個生成する
    var nonce = new byte[16];

    for (var i = 0; i < 3; i++) {
      rng.GetNonZeroBytes(nonce);

      Console.WriteLine(BitConverter.ToString(nonce));
    }
  }
}
RandomNumberGenerator.GetNonZeroBytesメソッドでバイト配列を非ゼロの乱数で満たす
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    Using rng As RandomNumberGenerator = RandomNumberGenerator.Create()
      ' RandomNumberGeneratorを使って0を含まない、16バイトのnonce値を3個生成する
      Dim nonce(15) As Byte

      For i As Integer = 1 To 3
        rng.GetNonZeroBytes(nonce)

        Console.WriteLine(BitConverter.ToString(nonce))
      Next
    End Using
  End Sub
End Class
実行結果の一例
FE-E5-5B-48-5F-4C-8E-8A-1C-AE-58-51-A2-8C-E4-A0
2C-69-84-60-A9-67-B1-27-CD-BA-23-68-57-A0-D3-CE
01-81-99-64-30-52-67-2F-41-95-0A-A0-F8-39-A7-07

整数乱数・値域を指定した乱数の生成 (GetInt32)

RandomNumberGeneratorから整数形式の乱数を生成したい場合は、GetInt32メソッドを使うことができます。 このメソッドは静的メソッドであるため、インスタンスを生成せずに使用することができますが、逆に特定の乱数ジェネレーターインスタンスに対して使用することができません。 GetInt32メソッドは.NET Standard 2.1/.NET Core 3.0以降で使用することができます。

GetInt32メソッドでは、Random.Nextメソッドと同様に、生成したい乱数の値域を指定します。 GetInt32メソッドでは次の2つのオーバーロードが用意されていて、いずれも得られる乱数の範囲は半開区間(n以上m未満)となっています。

GetInt32(toExclusive)
0以上・toExclusive未満の乱数を取得する。 [0, toExclusive)
GetInt32(fromInclusive, toExclusive)
fromInclusive以上・toExclusive未満の乱数を取得する。 [fromInclusive, toExclusive)

引数fromInclusive, toExclusiveは、どちらも負数を指定することができ、負の整数乱数を生成することもできます。

RandomNumberGenerator.GetInt32メソッドで整数の乱数を生成する .NET Standard 2.1/.NET Core 3.0
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    // 1〜6までの整数乱数を20個生成して表示する
    for (var i = 0; i < 20; i++) {
      Console.Write("{0}, ", RandomNumberGenerator.GetInt32(1, 6 + 1));
    }
    Console.WriteLine();
  }
}
RandomNumberGenerator.GetInt32メソッドで整数の乱数を生成する .NET Standard 2.1/.NET Core 3.0
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    ' 1〜6までの整数乱数を20個生成して表示する
    For i As Integer = 1 To 20
      Console.Write("{0}, ", RandomNumberGenerator.GetInt32(1, 6 + 1))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果の一例
3, 1, 2, 4, 6, 1, 2, 3, 4, 2, 2, 4, 4, 5, 5, 2, 3, 2, 5, 5, 

Random.NextDoubleに相当する、実数型の乱数を生成するメソッドは用意されていません。 GetInt32メソッドが使用できない場合や、実数型の乱数を生成したい場合は、RandomNumberGeneratorから生成したバイト列を型変換して目的の型・値域の乱数に変換する、などの方法をとる必要があります。

RandomNumberGeneratorで生成した乱数列を整数の乱数に変換する
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    using (var rng = RandomNumberGenerator.Create()) {
      // 1〜6までの整数乱数を20個生成して表示する
      for (var i = 0; i < 20; i++) {
        // RandomNumberGeneratorから4バイト分の乱数を取得する
        var uint32rand = (Span<byte>)stackalloc byte[4];

        rng.GetBytes(uint32rand);

        // 乱数バイト列を1から6までの整数に変換
        // (バイト列乱数をUInt32型に変換してから、double[0.0, 1.0)→int[1, 6]に変換する)
        var val = 1 + (int)(6.0 * (BitConverter.ToUInt32(uint32rand) / ((double)uint.MaxValue + 1.0)));

        Console.Write($"{val}, ");
      }

      Console.WriteLine();
    }
  }
}
RandomNumberGeneratorで生成した乱数列を整数の乱数に変換する
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    Using rng As RandomNumberGenerator = RandomNumberGenerator.Create()
      ' 1〜6までの整数乱数を20個生成して表示する
      Dim uint32rand(3) As Byte

      For i As Integer = 1 To 20
        rng.GetBytes(uint32rand)

        ' 乱数バイト列を1から6までの整数に変換
        '/ (バイト列乱数をUInt32型に変換してから、double[0.0, 1.0)→int[1, 6]に変換する)
        Dim val As Integer = 1 + CInt(Math.Truncate(6.0 * BitConverter.ToUInt32(uint32rand, 0) / (UInteger.MaxValue + 1.0)))

        Console.Write($"{val}, ")
      Next

      Console.WriteLine()
    End Using

  End Sub
End Class
実行結果の一例
3, 4, 1, 5, 1, 1, 5, 3, 4, 3, 2, 1, 1, 5, 5, 1, 2, 6, 2, 3, 

RNGCryptoServiceProviderクラス

RandomNumberGeneratorクラスの具象クラスとして、RNGCryptoServiceProviderクラスが存在します。 RNGCryptoServiceProviderクラスを以下のように直接インスタンス化して暗号乱数ジェネレータとして使用することもできます。

RNGCryptoServiceProviderクラスを暗号乱数ジェネレータとして使用する
using System;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    using (var rng = new RNGCryptoServiceProvider()) {
      var nonce = new byte[16];

      rng.GetBytes(nonce);

      Console.WriteLine(BitConverter.ToString(nonce));
    }
  }
}
RNGCryptoServiceProviderクラスを暗号乱数ジェネレータとして使用する
Imports System
Imports System.Security.Cryptography

Class Sample
  Shared Sub Main()
    Using rng As New RNGCryptoServiceProvider()
      Dim nonce(15) As Byte

      rng.GetBytes(nonce)

      Console.WriteLine(BitConverter.ToString(nonce))
    End Using
  End Sub
End Class

.NET Frameworkでは、RandomNumberGenerator.Createメソッドで作成されるデフォルトの乱数ジェネレータの実装はRNGCryptoServiceProviderクラスとなっています。 RandomNumberGenerator.Createメソッドでは、引数として生成する暗号乱数ジェネレータの名前を指定することもできるようになっていますが、デフォルトの状態ではRNGCryptoServiceProvider以外に生成できるジェネレータは用意されていないようです。

.NET Core/.NET 5では、RNGCryptoServiceProviderは独自の実装を持たず、単にRandomNumberGenerator.Createメソッドがデフォルトで生成する暗号乱数ジェネレータのラッパーとして動作します。

なお、.NET Frameworkで実装されているRNGCryptoServiceProviderクラスは、

暗号化サービス プロバイダー (CSP : Cryptographic Service Provider) によって提供された実装を使用して、暗号乱数ジェネレーター (RNG : Random Number Generator) を実装します。

RNGCryptoServiceProvider クラス

とされているため、乱数の生成にCryptGenRandom関数を使っていると思われます。 Monoの実装では、/dev/random, /dev/urandomもしくはWindows上ではCryptGenRandom関数を使用しているようです。

.NET Core/.NET 5での実装(RandomNumberGenerator.Createメソッドが生成するデフォルトの暗号乱数ジェネレーター)では、WindowsではBCryptGenRandom関数、Linux等ではOpenSSLのRAND_bytes関数が使われているようです。

Guid.NewGuidメソッド

Guid構造体自体は乱数ジェネレータではありませんが、NewGuidメソッドは一意なGUIDを返すために乱数を使用しているため、このメソッドの戻り値は一種の乱数とみることもできます。 単にランダムなIDが必要な場合には、Randomクラスを用いて生成せずとも、直接GUIDを使うことができます。

なお、Guid構造体を乱数として用いる場合は、variantフィールドやバージョン番号などの一部のビットは常に同じ値となる点に注意が必要です。

Guid.NewGuidメソッドでランダムなIDを生成する
using System;

class Sample {
  static void Main()
  {
    // ランダムなGUIDを10個生成する
    for (var i = 0; i < 10; i++) {
      Console.WriteLine(Guid.NewGuid());
    }
  }
}
Guid.NewGuidメソッドでランダムなIDを生成する
Imports System

Class Sample
  Shared Sub Main()
    ' ランダムなGUIDを10個生成する
    For i As Integer = 1 To 10
      Console.WriteLine(Guid.NewGuid())
    Next
  End Sub
End Class
実行結果の一例
19e95248-34ac-42b9-970a-61763322fd7e
c2a96c19-cfbc-4376-b39e-7bc86cd59be6
5d18bb4a-0939-410f-a51f-9d600ee9075e
a23e5662-afda-4534-9409-212110e4816b
b25dba03-fbce-4f53-8d09-bfa3a057e50c
4753f00e-feaf-4f8d-b719-3027baaec197
5abb53c2-fc0e-4013-96a2-06f7ea9b642a
89c42aeb-3f5e-4165-8c8b-a4a6a27565ab
b2c125db-2ecd-4cc3-b3ca-72ff491dd318
faa9345c-1e84-4f7d-8338-7969e97a5cdb

この結果にも現れているとおり、xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxxのビットは常に同じ値となります。