うめすこんぶ

日々のプログラミングで残しておきたいメモ.何かの役に立てれば幸いです.

javascriptで配列をシャッフルする関数

スポンサーリンク

配列内の要素をカードのシャッフルのようにでたらめな順序にする関数, shuffleを作ってみます. 簡単な例から行きます.

簡単な例

とりあえず,現実でカードをシャッフルすることを考えてみましょう. カードの中から適当に2枚を選んで,交換.これを何回も繰り返せば適当な順序にカードが並ぶでしょう. というわけでjavascriptでソースを書いてみます.

/* min以上,max以下のランダムな数を出力 */
function rand(min, max){ return min + ~~(Math.random() * (max - min + 1));};

function shuffle(x){
  for(var i = 0; i < 10; i++){
    var j =  rand(0, x.length - 1);
    var k =  rand(0, x.length - 1);
    var temp = x[j];
    x[j] = x[k];
    x[k] = temp;
  }
}

ループを使用して,すごく適当に10回カードの交換を行います.

しかし,これでは,もしかしたら同じカードの組ばかり交換しあって,結果として最初の順序と同じものが出来上がってしまう,ということもありえます. では,交換回数を10かいでなくてもっと増やせばいい,ということになると,それだけ時間がかかりますね.

頑張る

もっといい方法を作ってみます.今度は要素数分の交換で済む短時間のシャッフルです.

function shuffle(x){
  for(var i = 0; i < x.length - 1; i++){
    var j =  rand(i, x.length - 1);
    var temp = x[j];
    x[j] = x[i];
    x[i] = temp;
  }
}

やってる処理としては,まずループ内で,今見ているところ(インデックスiの位置) より前はシャッフルが済んだところにして,i以上のところはシャッフルの対象,と区切りをつけています. 今見ているところiのカードと,それ以降のカードから適当にrand()で選んだカードを交換しています.これをやってみると,たった要素数分の交換でもかなりデタラメにシャッフルされていました.

さらにステップ数を減らしたもう一つのシャッフル関数も載せておきます.

もっと簡単?な例

function shuffle(x){
  for(var i = 0, y = [], len = x.length; i < len; i++)
    y.push( x.splice( Math.random() * x.length | 0, 1)[0]);
  return y;
}

新しい配列yを作って,xの配列から適当に選んだ要素をsplice関数で抜き取り,yに追加. これをxの配列の中身がなくなるまで繰り返します.文にすると,わりと単純ですね. 自分で作ったrand関数も使ってないので,若干見づらいコードかも.