Macromedia Flash非公式テクニカルノート 配列を偏りなくランダムに並べ替える
予め決まったデータから、ランダムに値を得たいことがあります。とくに、複数の値を重複なく取出したいときには、値を配列に入れてランダムに並べ替えるのがスマートです。シャッフルしたカードを配るのと同じで、あとは頭から順に使えばよいからです。 01 配列をランダムに並べ替える
スクリプト001第2行目は、配列の長さ(Array.lengthプロパティ)を取得します。エレメントの数にもとづき、取出すランダムな位置のインデックス番号を計算するためです。 スクリプト001第3行目から、配列のエレメントすべてをwhileステートメントで繰返し処理します。ループの継続条件は、論理式でなく変数(i)になっています。変数が数値の場合、0以外の値はtrueと評価されます。変数(i)の値は、第4行目で1ずつ減算(ポストデクリメント)されます。したがって、変数値が0になるまで、エレメント数(配列の長さ)と同じ回数処理が行われることになります。 スクリプト001第4行目は、並べ替えるインデックス番号をランダムに定めます[*1]。得られる値は、0以上変数(i)値未満の整数です。変数(i)の初めの値は配列の長さで、配列の最後のエレメントのインデックスより1大きいです。したがって、0から最後のインデックス番号までの範囲の値がランダムに決まります。ただし、変数(i)の値は前述のとおり1ずつ減っていくので、繰返すたびに範囲は狭まります。 そして、スクリプト001第5〜7行目で、配列の最後からエレメントを遡って順に抜出し、ランダムに決められたインデックス番号のエレメントと差替えてゆきます。つぎのループでは変数(i)の値が1減って、ランダムなインデックスの範囲がひとつ狭まります。そのため、ひとたび差替えられたエレメントは、その位置を動くことがありません。後述するように、ここがポイントです。
02 並べ替えの偏り
前掲スクリプト001との大きな違いは、配列の長さを別の変数(n)にとったことです(第2行目の後に追加)。そして、この変数にもとづいてランダムなインデックスを計算しています(第4行目)。すると、ランダムなインデックスの値の範囲は、いつもエレメントの初めから最後までとなり、ループの間ずっと変わりません。 学校の教室で席替えするときのくじ引きを例にして考えてみましょう。普通、教室の座席につけた番号のくじをつくり、ひとりひとり順に引いてその席に座ります。何の問題もありません。前掲スクリプト001は、これと同じ考え方にもとづきます。ひとたび席が決まれば、その位置を動かないのです。 けれど、後から書替えたスクリプトでは、引いたくじを毎回戻すのと同じです。もし、前の人がすでに座っている席番号を後の人が引いたら、席を入替えます。つまり、すべての人がすべての座席番号を引く可能性をもちます。逆に、最後のひとりがくじを引き終わるまで、誰も座席が確定しません。 それだけでしたら、煩わしいだけで済ますこともできるでしょう。けれど前述のとおり、この処理方法ではランダムな並べ替えに偏りが出かねません。簡単にするために、3人のくじ引きを考えます。 3人ですので、席は3つです。くじを毎回戻しますので、各自3つの席番号を引く可能性があります。すると、すべての場合を数え上げれば、27(= 3×3×3)とおりの引き方があります。しかし、3人の席の座り方は順列の6(= 3! = 3×2×1)とおりしかありません。27を6で割ると3余ります。つまり、3人の座り方(6とおり)のうち3つが他の3つよりも多く出ることになるのです[*2]。 煩わしいうえに偏りが出るのはいただけません。そこで、毎回くじを戻すのではなく、ひとり引くたびにくじが減るのと同じように、ランダムな値の範囲もひとつずつ狭めなければならないのです。
作成者: 野中文雄 Copyright © 2001-2011 Fumio Nonaka. All rights reserved. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||