JavaScriptで複数ランダム値取得関数とビンゴゲームを作ってみた

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

ある意味、前回の続き(配列から重複なくランダムに複数の値を得る方法 | while(isプログラマ))。

配列から指定の数分、ランダムで値を取得する関数を作ってみました。

第一引数にオプションを、第二引数にランダムの値を得る元の配列をいれます。

第一引数のオプションの説明は下記のとおりです。

  • size:取得するランダム値の数。初期値は1
  • min:配列を指定しない場合に取得するランダム値の最小値。初期値は1
  • max:配列を指定しない場合に取得するランダム値の最大値。初期値は100
  • step:配列を指定しない場合に取得するランダム値取得用の数列の値の差。初期値は1

例:

ランダムの値を配列から複数取得するための関数ですが、sizeを0として、第二引数に長さが0の配列をいれると等差数列の配列を取得するということもできます。

最後の結果が想定と違う結果になってしまったのは、JavaScriptが浮動小数点型で計算しているためです。小数点の演算を扱うとたまに遭遇します(参考:参考:Javascriptで小数を含む計算を行うとき、計算結果の小数点2位以下を丸める。 | Scenee’s blog)。

さて、関数を作ってみただけではなんなのでその関数の実用的な(かどうかは分かりませんが)サンプルを作ってみました。
JavaScriptでビンゴゲーム

ビンゴゲームです(本来、一人で楽しむためのものではないですが・・・)。
例えばビンゴシートのそれぞれの列を埋めるのに今回作成した関数を使っています。例えば、最も左の列をランダムな値で埋めるのは以下のようにやっています。

writeBingoSeatは第一引数にランダム値を入れる列を表すjQueryオブジェクトを入れて第二引数に数値配列を入れるとビンゴシートに数値を埋めていくメソッドです。第二引数には最小値が1、最大値が15の範囲の整数を5つ取得した配列を入れています。

他には、ビンゴの番号を選ぶのにも今回作成した関数を使っています。例えば以下の様な感じ。

詳しく知りたい方はページのソースを見てみてください。

こういうのは本来オブジェクト指向で書いたほうがいいんでしょうけどね・・・。まだまだオブジェクト指向はよく分かってません・・・。上司がJavaScriptをオブジェクト指向で書くようになったので、自分もそろそろ本格的にオブジェクト指向を勉強したほうがよさそうなんですが・・・。

配列から重複なくランダムに複数の値を得る方法

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

カテゴリーはJavaScriptでコードもJavaScriptですが、どちらかというとアルゴリズム寄りの話です。たいがいのプログラミング言語では同様の方法で実装できる方法だと思います。

配列からランダムに複数の値を得たいということがあると思うのですが、普通にランダム関数でランダムの値を取得して、その配列のインデックスの値を取得しても重複してしまいます。
例えば下記のようなソースコード。

上記のコードは、arrという名前の配列からランダムに値を5つ取得してrandArrという配列に格納していくというプログラムですが、コンソールの結果は下記のようになりました。

randArrの中の値は重複せずに入っていることもありますが、かなりの確率で重複してしまいます。

じゃあ、重複せずにランダムで複数の値を取得するにはどうすればいいか考えてみると、最初に配列をシャッフルしてシャッフルした後の最初の5つを取得すれば重複せずに取得できます。ただ、全体をシャッフルしなきゃいけないので無駄が多そうです。なお、余談ですが、ランダムシャッフルはFisher-Yates法がよさそうです(参考:svartalfheim.jp – 配列を少ない仕事量でシャッフルするFisher-Yates法)。

ところで、よくよく考えたら複数の値を取得する配列が変更可能であれば、ランダムに取得した値はなくして大丈夫なわけです。ということは、ランダムで得た値の箇所をランダム値の上限のインデックス(1回目は配列の最後の要素)で置き換え、次に取得するランダム値の範囲を1減らせばランダムで取得する範囲はすでに取得した値以外になるわけです。
例えば、下記のようなサンプルコード。

コンソールには下記のように表示されました。

元々の配列がかなり変更することになりますが、重複なく値を取得できています。ループ回数も取得したい値の数(上記の例では5)だけですみました。

上記はJavaScript以外のプログラミング言語のことも考えて実装したアルゴリズムです。実は、JavaScriptだと配列の指定の箇所を取り除くspliceというメソッドがあるので、それを使うと上記のプログラムは下記のように実装できます(参考:Array.splice – JavaScript | MDN)。

コンソールの結果は下記のようになりました。

こちらだと元々の値を取得する配列は要素が減るだけなので、スッキリしています。もし、ランダム値を取得する順番をとわないのであれば、ランダムに値を減らした元々の配列に入っている値をランダムに取得した値と考えることもできそうです(例えば、100個の値が入っている配列からランダムに90個取得する場合、ループは90回でなく10回でいい)。

2014/05/13 23:39追記:spliceメソッドの戻り値は配列なので、0番目の値を入れるように指定しました。