こんにちは。
乱数は奥が深い。
只野です。
はい。
プログラムやっていると「ランダムで数字が欲しい!!」なんて事ありますよね。
主にゲーム系のプラグラムになるかと思います。
私はゲーム作れないんですけどね!!
もっぱら業務システム系のエンジニアですから私。
DBにあれが入ってこれが入ってこう出すんですね。業務的にここはほにゃほにゃとかプログラム的には面白くないやつですよ。
プログラム自体は嫌いではないので、たまに遊びで何か作ってみたくなる私です。そんな時にランダムな値使えれば面白いですよね(たぶん)
こんな感じでC#でランダムな値を使って何か作ってみようって事です。
まずは何を作るかですね。
ここでゲームとか作れればかっこよいんでしょうね。
先ほども言ったように私はゲーム作れません。いずれ作ります。はい。
そんな私が本日作るのは“ロト番号自動選択ツール”です。
ロト系の宝くじは決められた番号の中から任意で番号を選択して夢を買うゲームです。
確率的にまぁ当たらないですよね笑
ランダムと戦うためにはランダムしかないと謎理論を立てプログラムを作成するわけです。
疑似乱数(Random)を使ってロト番号自動選択ツールを作る。
まずは作るプログラムの仕様を考えます。
プログラム仕様
・WindowForm(C#)で作成
・ロト6とロト7どちらも対応
・ロト6は1~43の数字からランダムで6個選択
・ロト7は1~37の数字からランダムで7個選択
・ロト6、ロト7どちらも選択数字が被らないようにする
ざっとこんな感じですかね。
後は気分で作りましょう←
疑似乱数の説明
はい。
作っていく前にまず“疑似乱数”の説明です。
簡単に言うと“結果が読める”タイプの乱数になります。
どういうことだってばよ!と思った方もいると思います。
疑似乱数とは計算によりあたかも乱数のような振る舞いをする事を言います。
本物の乱数は結果が読めないタイプの事を言います。朝お腹が痛くなるタイミングが読めないみたいな感じです。
疑似乱数は最初に“シード”と呼ばれる計算の基準となる値を設定します。
“シード”を元に計算をしていき出力する値を生成するのです。
これが乱数っぽく見えるのです。
“シード”の値がわかってしまえば疑似乱数の場合、どの値が生成されるかわかります。
今回使用するC#のクラス“Random”は“疑似乱数”です。
ちゃんとした乱数じゃないならダメではないか!と思うでしょう。
大丈夫です。普通の人では結果は読めません。お遊びツールでしたら使いやすくお手頃です。
ただ使い方を間違えると毎回同じ動作をするだけになります。
例えば下記の様にソースを記載したとします。
//Randomのインスタンス生成時にシード値を引数にします。 Random random1 = new Random(1); Random random2 = new Random(1); //Nextメソッドで値を取得します。引数で取得す値の範囲が指定できます。 //今回は0~9の間で数字頂戴ね。です。※終了範囲は欲しい範囲+1で指定です。 int val1 = random1.Next(0,10); int val2 = random2.Next(0,10);
このソースを実行すると“val1とval2の取得結果が同一になります”
なぜかと言いますと上記で記載したように“シード”を元に計算により値を生成しているので、“Random”のインスタンスを生成した時のシードが同一だと同じ値が生成されるのです。
この後に“Next”メソッドを使用して値を取得しても、取得した回数が同じならばずっと同じ値となります。
実装
では実装です。
デザインはこうなりました。
そしてソースが
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //タブ設定 List<Control> tabs = new List<Control>(); tabs.Add(this.textBoxSeed); tabs.Add(this.panelLoto); tabs.Add(this.radioButtonLoto6); tabs.Add(this.radioButtonLoto7); tabs.Add(this.buttonRun); foreach (Control c in this.GetAllControls<Control>(this)) { c.TabStop = false; if (tabs.Exists(x => x.Equals(c))) { c.TabStop = true; c.TabIndex = tabs.IndexOf(c); } } this.radioButtonLoto6.Checked = true; this.textBoxSeed.KeyPress += TextBoxKeyPress; } /// <summary> /// 配下コントロール取得 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="top"></param> /// <returns></returns> private List<T> GetAllControls<T>(Control top) where T : Control { List<T> buf = new List<T>(); foreach (Control ctrl in top.Controls) { if (ctrl is T) buf.Add((T)ctrl); buf.AddRange(GetAllControls<T>(ctrl)); } return buf; } /// <summary> /// テキストボックスキープレス /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TextBoxKeyPress(object sender, KeyPressEventArgs e) { //0~9と、バックスペース以外の時は、イベントをキャンセルする if ((e.KeyChar < '0' || '9' < e.KeyChar) && e.KeyChar != '\b') { e.Handled = true; } } /// <summary> /// 実行ボタンクリック /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonRun_Click(object sender, EventArgs e) { if(string.IsNullOrEmpty(this.textBoxSeed.Text)) { MessageBox.Show("シードが入力されていません。"); } try { //疑似乱数 Random r = new Random(Convert.ToInt32(this.textBoxSeed.Text)); //選択番号範囲設定 int endnum = this.radioButtonLoto6.Checked ? 43 : 37; //番号選択回数 int endcnt = this.radioButtonLoto6.Checked ? 6 : 7; //番号リスト作成 List<int> nums = new List<int>(); for (int cnt = 1; cnt <= endnum; cnt++ ) { nums.Add(cnt); } //番号選択 List<int> rets = new List<int>(); for (int cnt = 0; cnt < endcnt; cnt++ ) { //取得番号生成 int index = r.Next(0, endnum - cnt); //番号選択 rets.Add(nums[index]); //番号削除※同一番号選択防止 nums.RemoveAt(index); } //並び替え(昇順) rets.Sort(); //結果出力 this.textBoxRet.Text = string.Empty; foreach (int num in rets) { this.textBoxRet.Text += num.ToString() + " "; } } catch (Exception ex) { MessageBox.Show(ex.Message); } } }
完成です。
今回は疑似乱数生成に必要になるシードを入力するタイプで作成してみました。
肝となる処理は75行目からです。
特殊な処理もなく、ソースにコメントを記載しているので細かく説明はしません。
このソースで出来上がったプログラムを実行すると
いい感じです。
それっぽい数字なので心なしか当たる気がしてきますね(気のせい)
本日はこの辺で