lycheejam's tech log

チラ裏のメモ帳 | プログラミングは苦手、インフラが得意なつもり。

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.TickCountMSDNによると

システム起動後のミリ秒単位の経過時間を取得します。
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

軽くシード値と乱数のログを取ってみました。
シード値は重複していないですね。

関連記事

kitigai.hatenablog.com

雑感

メソッド内で毎回Randomクラスを生成させるようにしたら若干動作が鈍くなった?
シード値のみを変更するプロパティも探してみたんですが
目が節穴だったんですかね?

これに関連させて文字列結合部分も修正したんでStringBuilder使ったよって記事を書きたいです。