セージ の メモ書き

メモこそ命の恩人だ

C# - 乱数(Random/RandomNumberGenerator クラス)

Random クラス

docs.microsoft.com

Random.Next メソッド

Random.Next メソッド (System) | Microsoft Docs

  • 引数に指定した範囲の乱数を生成する。
  • "minValue 以上、maxValue 未満" の乱数を生成する。
  • 注意:"maxValue 未満" なので、+1 が必要か確認する。
var random = new Random();

foreach (var item in Enumerable.Range(0, 5))
{
    // 以下の場合、1~3 の乱数が生成される。
    var value = random.Next(minValue: 1, maxValue: 4);
    Debug.Write($"{value}, ");
}
// 2, 1, 3, 3, 1
// 1~3 の乱数になったことを確認

Random.NextBytes メソッド

Random.NextBytes メソッド (System) | Microsoft Docs

  • バイト配列に乱数を格納できる。
  • バイトなので 0~255 の数値が格納される。
void Log(byte[] data)
{
    data.ToList().ForEach(x => Debug.Write($"{x} "));
    Debug.WriteLine("");
}

var random = new Random();
var byteData = new byte[10];

random.NextBytes(byteData);
Log(byteData);
// 70 140 219 233 46 79 208 197 50 244

random.NextBytes(byteData);
Log(byteData);
// 37 108 35 55 207 28 200 126 51 237

random.NextBytes(byteData);
Log(byteData);
// 228 49 46 210 154 236 111 254 177 215

// バイト配列に乱数が格納されたことを確認。
// テストデータ作る時とか、何かに使えそう。

Random.NextDouble メソッド

Random.NextDouble メソッド (System) | Microsoft Docs

  • 0.0 以上 1.0 未満の乱数を生成できる。
var random = new Random();

Debug.WriteLine(random.NextDouble());
// 0.14098222359608426
Debug.WriteLine(random.NextDouble());
// 0.030359947923155994
Debug.WriteLine(random.NextDouble());
// 0.2668691195922127

// ランダムな小数を生成できたことを確認。


RandomNumberGenerator クラス

docs.microsoft.com

  • Random クラスの場合、乱数の生成アルゴリズムが公開されている。
    • 乱数を予測できる。らしい。
    • セキュリティ分野には向かない場合もある。
  • RandomNumberGenerator クラスの場合 ...
    • 乱数生成アルゴリズムの結果に対し、さらに別の処理が追加されている。
    • ランダムかつ予測できない値が得られる。
  • 以下の参照が必要。
    • using System.Security.Cryptography;
  • GetInt32 メソッドで生成する。
    • 使用感は、Random.Next メソッドと同じ。
var random = new Random();

foreach (var item in Enumerable.Range(0, 5))
{
    // 以下の場合、1~3 の乱数が生成される。
    var value = RandomNumberGenerator.GetInt32(fromInclusive: 1, toExclusive: 4);
    Debug.Write($"{value}, ");
}
// 3, 2, 2, 3, 3, 
// 1~3 の乱数になったことを確認


実験

実験1:シードを揃えると同じ乱数になる?

// 同一シード
var random1 = new Random(Seed: 100);
var random2 = new Random(Seed: 100);

foreach (var item in Enumerable.Range(0, 5))
{
    var value1 = random1.Next(minValue: 1, maxValue: 4);
    var value2 = random2.Next(minValue: 1, maxValue: 4);
    Debug.Write($"{value1}-{value2}, ");
}
// 3-3, 1-1, 3-3, 3-3, 2-2, 
// 同一シードの場合、乱数も同一になった。
// 異なるシード
var random1 = new Random(Seed: 100);
var random2 = new Random(Seed: 200);

foreach (var item in Enumerable.Range(0, 5))
{
    var value1 = random1.Next(minValue: 1, maxValue: 4);
    var value2 = random2.Next(minValue: 1, maxValue: 4);
    Debug.Write($"{value1}-{value2}, ");
}
// 3-1, 1-2, 3-2, 3-1, 2-2, 
// 異なるシードの場合、異なる乱数になった。

実験2:乱数に偏りはある?

// 1~3 の乱数を生成する
public void CreateRandom(int? seed, int count)
{
    var random = seed.HasValue ? new Random(seed.Value) : new Random();
    var randomList = new List<int>();
    foreach (var item in Enumerable.Range(0, count))
    {
        var value = random.Next(minValue: 1, maxValue: 4);
        randomList.Add(value);
    }
    
    Debug.WriteLine($"" +
        $"[1]{randomList.Count(x => x == 1)}, " +
        $"[2]{randomList.Count(x => x == 2)}, " +
        $"[3]{randomList.Count(x => x == 3)}");
}
// シード100で試す。偏りは、ほぼなし。

CreateRandom(seed: 100, count: 100);
// [1]26, [2]41, [3]33

CreateRandom(seed: 100, count: 1000);
// [1]311, [2]342, [3]347

CreateRandom(seed: 100, count: 10000);
// [1]3298, [2]3270, [3]3432

CreateRandom(seed: 100, count: 100000);
// [1]33075, [2]33279, [3]33646

CreateRandom(seed: 100, count: 1000000);
// [1]333527, [2]333176, [3]333297
// シード1000で試す。偏りは、ほぼなし。シードは関係ない。

CreateRandom(seed: 1000, count: 100);
// [1]34, [2]32, [3]34

CreateRandom(seed: 1000, count: 1000);
// [1]363, [2]314, [3]323

CreateRandom(seed: 1000, count: 10000);
// [1]3400, [2]3285, [3]3315

CreateRandom(seed: 1000, count: 100000);
// [1]33597, [2]33147, [3]33256

CreateRandom(seed: 1000, count: 1000000);
// [1]333709, [2]333061, [3]333230
// シード未指定で試す。これも、偏りはほぼなし。

CreateRandom(seed: null, count: 100);
Thread.Sleep(1000);
// [1]31, [2]36, [3]33

CreateRandom(seed: null, count: 1000);
Thread.Sleep(1000);
// [1]341, [2]331, [3]328

CreateRandom(seed: null, count: 10000);
Thread.Sleep(1000);
// [1]3269, [2]3370, [3]3361

CreateRandom(seed: null, count: 100000);
Thread.Sleep(1000);
// [1]33340, [2]33287, [3]33373

CreateRandom(seed: null, count: 1000000);
Thread.Sleep(1000);
// [1]332796, [2]333538, [3]333666

上記は "1~3" の範囲で実験したが、"1~2"、"1~10" で試しても同様の結果であった。



以上