最近ミニゲームをいくつか試作しているのですが、その中で可変DBをランダムにシャッフルしたい場面が出てきました。そこで、有名なシャッフルのアルゴリズムである「フィッシャー–イェーツのシャッフル」を使って、可変DBをシャッフルするコモンを作ってみました。
フィッシャー–イェーツのシャッフルとは
簡潔に言うと、要素数Nの配列について次の操作を行います。
- 要素0からL(Lの初期値はN-1)の範囲から、1つの要素をランダムに選ぶ
- 1.で取り出した要素をL番目の要素と交換する。
- Lを-1する
- Lが0になるまで1~3を繰り返す。
例えば、次のような配列があるとします。
要素[0] | 要素[1] | 要素[2] | 要素[3] |
1 | 2 | 3 | 4 |
このときN=4、L=3です。フィッシャー–イェーツのシャッフルの1回目だけトレースしてみると、
- 要素[0]~[3]のうち、ランダムな要素を1つ選ぶ。ここでは要素[0]が選ばれたとする。
- 要素[0]と要素[3]の値を入れ替える。(つまり、1と4を交換する)
- Lを-1するので、L=2となる。
という風になります。同じ値が2度選ばれることがないのがポイントです(上の例なら、2回目以降は1は抽選の範囲外になるので選ばれることはない)。
このアルゴリズムには次のような特徴があります。
- シンプルかつ効率的
- (使用する乱数に偏りがなければ)生成結果に偏りがない
フィッシャー–イェーツのシャッフルは、言われてみれば「なるほどな」と理解できる簡潔さにもかかわらず非常に強力です。早い、偏りがない、シンプルと3拍子揃った秀逸なアルゴリズムですね。
可変DBをシャッフルするコモンイベント
サンプルとして、引数に指定した可変DBの項目0番を入れ替えるコモンイベントを作りました。
■DB読込(可変): CSelf10[データ数] = 可変DB[タイプCSelf0[可変DB番号](-)のデータ数] ▼ ■変数操作: CSelf11[抽選範囲] = CSelf10[データ数] - 1 ■ループ開始 |▼ |▼ 抽選 |■変数操作: CSelf12[抽選結果] = 0 ~ CSelf11[抽選範囲] |▼ |▼ 交換 |■DB読込(可変): CSelf20[一時変数A] = 可変DB[ CSelf0[可変DB番号] : CSelf11[抽選範囲] : 0 ] (- : - : ×NoData) |■DB読込(可変): CSelf21[一時変数B] = 可変DB[ CSelf0[可変DB番号] : CSelf12[抽選結果] : 0 ] (- : - : ×NoData) |■可変DB書込:DB[ CSelf0[可変DB番号] : CSelf11[抽選範囲] : 0 ] (- : - : ×NoData) = CSelf21[一時変数B] |■可変DB書込:DB[ CSelf0[可変DB番号] : CSelf12[抽選結果] : 0 ] (- : - : ×NoData) = CSelf20[一時変数A] |▼ |■変数操作: CSelf11[抽選範囲] -= 1 + 0 |▼ |■条件分岐(変数): 【1】 CSelf11[抽選範囲] が 0 以下 |-◇分岐: 【1】 [ CSelf11[抽選範囲] が 0 以下 ]の場合↓ | |■ループ中断 | |■ |◇分岐終了◇ |■ ◇ループここまで◇◇ ▼
アルゴリズムがシンプルなので、短いコードで簡単に記述できます。