別々のSystem.Randomインスタンスが同じ乱数列を生成してしまう

__注意: C#初心者なので、とんちんかんなことをいっているかもしれません。__呼び出すたびに5桁のランダムな整数からなるパスワードを生成する`CreatePassword`というメソッドを作成。5回連続でこのメソッドを呼び出したところ、5回とも同じパスワードが生成されるという事象が発生しました(´・ω・｀)

```csharp
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(CreatePassword()); // => 83740
        Console.WriteLine(CreatePassword()); // => 83740
        Console.WriteLine(CreatePassword()); // => 83740
        Console.WriteLine(CreatePassword()); // => 83740
        Console.WriteLine(CreatePassword()); // => 83740
    }

    static string CreatePassword()
    {
        var random = new Random();
        List<int> digits = new List<int>();
        while (digits.Count < 5)
        {
            digits.Add(random.Next(10));
        }
        return string.Join("", digits);
    }
}
```

そこでインターネットの叡智にすがってみました。そもそも`System.Random`のコンストラクタは`Random()`と`Random(int32)`の2種類があります。後者は引数としてシード値を与えているのですが、引数を省略した場合(つまり前者の場合)、シード値はPCが起動してからの時間を保持する`System.Environment.TickCount`が使用されます。要するに`new Random()`は内部的には`new Random(System.Environment.TickCount)`と同じということになります。

__問題はこの`TickCount`プロパティがミリ秒単位であるということ。つまり1ミリ秒以内に複数の`Random`インスタンスがコンストラクタ`Random()`により生成された場合、すべて同じシード値になり、結果として同じ乱数列が生成されてしまいます。__

そこで以下のように`sleep`を利用して、`Random()`のシード値である`TickCount`の値を無理やり変えてやると、`sleep`の前と後ろで乱数列が異なる、つまりシード値が変化したような挙動を見せています。前3つと後3つはそれぞれ1ミリ秒以内に生成され、同じシード値を共有しているのでしょう。(コンパイラの最適化なども「悪さ」をしているような気がしますが、そこまでは追いきれませんでした……)

```csharp
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(CreatePassword()); // => 38067
        Console.WriteLine(CreatePassword()); // => 38067
        Console.WriteLine(CreatePassword()); // => 38067

        System.Threading.Thread.Sleep(1000); // sleep in 1 sec.

        Console.WriteLine(CreatePassword()); // => 72012
        Console.WriteLine(CreatePassword()); // => 72012
        Console.WriteLine(CreatePassword()); // => 72012
    }
    
    static string CreatePassword()
    {
        var random = new Random();
        List<int> digits = new List<int>();
        while (digits.Count < 5)
        {
            digits.Add(random.Next(10));
        }
        return string.Join("", digits);
    }
}
```

__原因が分かったとして、では冒頭のコードを想定通り動作させるにはどう修正すればよいかですが、不用意に`System.Random`インスタンスを生成せずに使いまわすというのが手っ取り早そうです。__繰り返すように`Random()`の乱数らしさは「時間」に依存していますが、この「時間」をプログラム上でコントロールするのは容易ではない。だとすれば「時間」の流れによって変化しないロジック、すなわち初期化された状態のままのインスタンスを使いまわすロジックを採用するのは、そう悪くない戦略だと思います。

```csharp
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(CreatePassword()); // => 60926
        Console.WriteLine(CreatePassword()); // => 54621
        Console.WriteLine(CreatePassword()); // => 62504
        Console.WriteLine(CreatePassword()); // => 67956
        Console.WriteLine(CreatePassword()); // => 91760
    }
    
    static Random random = new Random();

    static string CreatePassword()
    {
        List<int> digits = new List<int>();
        while (digits.Count < 5)
        {
            digits.Add(random.Next(10));
        }
        return string.Join("", digits);
    }
}
```

確かに修正後は`CreatePassword`をコールするたびに、ランダムなパスワードが生成されていますね(｀・ω・´)ｼｬｷｰﾝ

参考:

- https://msdn.microsoft.com/ja-jp/library/system.random(v=vs.110).aspx
- https://msdn.microsoft.com/ja-jp/library/system.environment.tickcount(v=vs.110).aspx
- http://www.atmarkit.co.jp/fdotnet/dotnettips/035random/random.html