【ウディタ】可変DBをランダムにシャッフルする

最近ミニゲームをいくつか試作しているのですが、その中で可変DBをランダムにシャッフルしたい場面が出てきました。そこで、有名なシャッフルのアルゴリズムである「フィッシャー–イェーツのシャッフル」を使って、可変DBをシャッフルするコモンを作ってみました。

フィッシャー–イェーツのシャッフルとは

簡潔に言うと、要素数Nの配列について次の操作を行います。

  1. 要素0からL(Lの初期値はN-1)の範囲から、1つの要素をランダムに選ぶ
  2. 1.で取り出した要素をL番目の要素と交換する。
  3. Lを-1する
  4. Lが0になるまで1~3を繰り返す。

例えば、次のような配列があるとします。

要素[0] 要素[1] 要素[2] 要素[3]
1 2 3 4

このときN=4、L=3です。フィッシャー–イェーツのシャッフルの1回目だけトレースしてみると、

  1. 要素[0]~[3]のうち、ランダムな要素を1つ選ぶ。ここでは要素[0]が選ばれたとする。
  2. 要素[0]と要素[3]の値を入れ替える。(つまり、1と4を交換する)
  3. 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 以下 ]の場合↓
 | |■ループ中断
 | |■
 |◇分岐終了◇
 |■
◇ループここまで◇◇
▼

アルゴリズムがシンプルなので、短いコードで簡単に記述できます。