C# 乱数の生成
概要
過去に作成した疑似マイナンバー生成機を作って乱数を生成する処理を書いた。
ふとネットの記事を目に*1して「あれ?既存のコードじゃ重複した乱数生成されるんじゃ?」となったので直しました。
20180423 追記
このやり方めっちゃ間違ってます。別記事にて書き直しました。
kitigai.hatenablog.com
ソース
github.com
※コミットがたくさんありますがGitに慣れてなくていろいろやらかした結果です。
修正箇所
RandomNum.randNum
の乱数生成メソッドを修正しました。
Random(規定値)
修正前
class RandomNum { public Random r = new Random(); public int[] randNum(int digits) { int[] numArry = new int[digits]; for (int i = 0; i < digits; i++){ //0以上10未満のランダム整数を返す numArry[i] = r.Next(10); } //配列ごと返却 return numArry; } }
桁数を指定して指定された桁数分の乱数を生成して配列を返すメソッドです。
この場合クラス先頭でRandom
クラスを生成しています。
その際にRandom();
としてシード値となる引数を省略しています。
その為、Randomクラスのシード値は既定の数値が採用されます。
既定の数値はEnvironment.TickCount
プロパティが使用されます。
Environment.TickCount
はMSDNによると
システム起動後のミリ秒単位の経過時間を取得します。
Environment.TickCount Property (System) | Microsoft Docs
とあるので、単純理解でPC起動からの経過時間で良いと思います。
これを使用しているとPC起動からの経過時間ですのでシード値が重複する可能性があります。
そうするとシード値が同一である場合、乱数も同一のものが生成されるようです。
これを回避する為に下記のように修正しました。
Random(DateTime.Now.Ticks)
修正後
class RandomNum { public int[] randNum(int digits) { int[] numArry = new int[digits]; Random r = new Random((int)DateTime.Now.Ticks); for (int i = 0; i < digits; i++) { //0以上10未満のランダム整数を返す numArry[i] = r.Next(10); } //配列ごと返却 return numArry; } }
メソッド外でのRandom
クラス生成を辞め、メソッド内で生成しています。
そして一番の変更点がRandom
クラスの
引数(シード値)をDateTime
に変更しました。
DateTime.Now.Ticks
プロパティはMSDNによると
このプロパティの値が 0001 年 1 月 1 日の午前 12時 00分: 00 からの経過時間を 100 ナノ秒間隔の数を表します。
DateTime.Ticks Property (System) | Microsoft Docs
西暦0001年1月1日午前12時 00分から100ナノ秒単位でカウントアップしてる値らしいです。
これなら重複したシード値はできないので重複する乱数も生成されませんね。
Tick値:931672150 22642406896 Tick値:952348950 11534136790 Tick値:970618753 75469999486 Tick値:983841539 81082683967 //...中略 Tick値:1242322315 40226871652 Tick値:1245089554 15085967397 Tick値:1247829594 42195891078 Tick値:1250158784 29916914712 Tick値:1255451475 00451979972
軽くシード値と乱数のログを取ってみました。
シード値は重複していないですね。
雑感
メソッド内で毎回Randomクラスを生成させるようにしたら若干動作が鈍くなった?
シード値のみを変更するプロパティも探してみたんですが
目が節穴だったんですかね?
これに関連させて文字列結合部分も修正したんでStringBuilder使ったよって記事を書きたいです。