会社の先輩から「トランプをランダムに配るアルゴリズムを考えてみぃ」とクイズを出されたので帰りの電車で考えていました。で、なかなか面白かったのでここに記録。
疲れているうえに、もともとない頭を振り絞って考えたのが
- vector を使う方法(反則ぎみ)
- ランダムにソートして上から配る方法
の 2 つ。で、先輩から教えてもらったのが、
- 残りの札からランダムに選んだ 1 枚を配る方法
1 番目は論外なのでちょっと置いといて、2 番目と 3番目についてちょっと説明してみようと思います。
それぞれを shuffle2, shuffle3 とするとコードは
void shuffle2( int *card, int n ){
qsort( card, n, sizeof( *card ), cmp_rand );
}
int cmp_rand( const void *a, const void *b ){
return (rand() % 2 == 0) ? -1 : 1;
}
void shuffle3( int *card, int n ){
int tmp;
int i,j;
for( i = 0; i < n; i++ ){
j = rand() % (n - i ); /* (1) */
tmp = card[j];
card[j] = card[ n - i - 1];
card[ n - i ] = tmp;
}
}
こんな感じです。私は並べ替えに qsort 関数を使ってしまったのですが、ソートはあくまでも順序付けられた並べ替えなのであって、シャッフルするのには qsort は計算量が無駄すぎました。しかも、そこからカードを配る場合には、さらに n 回ループしなくてはならず…。まだまだ勉強不足です。
一方、shuffle3 の場合は配列要素のスワップの回数は n 回 だし、カードを配るコードを追加するにしても (1) の箇所に追加すれば新たにループする必要もありません。すべてのカードの位置が変わっているのもいいところです。
ちなみに perl の List::Utilモジュールにある shuffle も、Java の Collections クラスの shuffle メソッドも shuffle3 と同じ方法でした。
これからも精進してまいります…。













最近のコメント