RPGツクールと数学のブログ

RPGツクールと数学についてのブログです。

エンカウント確率分布

過去ブログの転載です。

昔ながらのRPGでは、歩いているとランダムに敵と遭遇して戦闘が始まりますよね。あれをランダムエンカウントと言いますが、具体的にどんなランダムであれば心地よく戦闘を迎えられるのか考えます。

過去ブログの投稿に沿った内容なので、カテゴリがツクール2000になっていますが……実際、2000で実装するにはきつい内容です。↓MV向けのプラグインを作りましたので、MVでの実装はこちらを使っていただければ一瞬です。

fermiumbay13.hatenablog.com

エンカウント確率分布

ランダムエンカウントは、何歩か歩いたら突然戦闘が始まる、というものになっています。そのため、シンプルには以下のような方法で実装されます。

(1) 一歩ごとに低確率の遭遇判定を設け、その確率になったとき遭遇

(2) 次に遭遇するまでの歩数をランダムに計算しておく

 RPGツクールで標準に使えるランダムな数は一様乱数です。これは、例えばランダムの範囲を1~10にすると、1~10の値がそれぞれ等しい確率で出るというものです。一様乱数を用いて以上の実装をすると、考えすぎかもしれませんが、それぞれに対して次のような問題が発生します。

(1) 一歩ごとに遭遇判定

 ⇒ 戦闘が終わってから数歩歩いただけで、また戦闘が始まってしまうことがある。

(2) エンカウント歩数をランダム

 ⇒ 歩数の下限や上限が気になる。例えば10歩以上30歩以下とすると、9歩までは確実に出ないし、遭遇なしに31歩以上歩けない。

とても些細ですけれど、いずれもイライラの要因になりうる問題です。

このような問題が発生してしまう原因は、一様乱数だからに尽きます。そうではなく、例えば歩数をランダムにするにしろ、5歩になる確率は10%、10歩になる確率は20%、みたいに、よくありそうな歩数になる確率が必然的に高くなるような乱数を作ればイライラすることなく自然にエンカウントできると考えられます。

そこで、ここでは有名な確率分布である正規分布を使います。

f:id:fermiumbay13:20171208003943p:plain

横軸\displaystyle xが歩数に相当し、縦軸\displaystyle yが遭遇確率に相当します。これは、平均\displaystyle \mu標準偏差\displaystyle \sigma正規分布で、平均と標準偏差は好きに決めることができます。それらを予め決めてやってこの分布に従った乱数を作ってやれば、理想のエンカウントを作れそうですね。平均は最も出現確率の高い歩数、標準偏差は大きくなると分布の幅が広がる定数を意味します(出現歩数にムラが出来る)。

一様乱数から正規分布を作る

一様乱数からこの分布に従った乱数を作る方法はいくつか考案されています。

(A) 一様乱数を大量に足し算して平均をとる(中心極限定理

(B) ボックス=ミュラー

(C) 一様乱数で面積を出し、その面積となるような積分範囲を求める

(A)は最も実装がお手軽ですが、平均や標準偏差を指定することが難しく、厳密さにこだわるのであれば適しません。しかし実際は十分良い手法ですし、恐らく大半はこの方法で作られています。今回は割愛です。

(B)は個人的なオススメです。一様乱数で得た数に対して、比較的簡単な関数の計算を行うだけで、正規分布に従った乱数が得られてしまうという奇跡の手法です。オススメですが、こちらも割愛です。ごめんね……

今回は(C)でやります。厳密なやり方ですが、実装が困難です。

積分を用いた正規乱数の生成

f:id:fermiumbay13:20171208004009p:plain

初めに、確率分布の面積を一様乱数で決めてしまいます。全体の面積の0%~100%のどれかを一様乱数で決めて、そのような面積になるときの範囲を計算するのです。

上図は一様乱数によって80%が出たときの例です。左端から歩数\displaystyle xまでの範囲の面積を計算して、全体の80%になるような\displaystyle xを求めたら、それがエンカウント歩数ということになります。このようにすれば、一様乱数から正規乱数を得ることが可能です。

問題はこの\displaystyle xをどう求めるかですが、簡単ではありません。正規分布の関数(確率密度関数といいます)を、積分範囲\displaystyle 0から\displaystyle x積分して、答えが一様乱数の結果になるような\displaystyle xを求めることになります。

エンカウント歩数の公式

平均\displaystyle \mu標準偏差\displaystyle \sigmaに従う正規分布に基づくエンカウント歩数\displaystyle xは、一様乱数\displaystyle A\,(-1 \lt A \lt 1)として次式から得られる。

\displaystyle x=\left\lfloor \sqrt{2}\sigma \,{\rm erf}^{-1}(A) + \mu +\frac{1}{2} \right\rfloor

 ただし、\displaystyle \left\lfloor \cdot \right\rfloorは床関数(小数点以下切り捨て)、\displaystyle {\rm erf}^{-1}(A)は逆誤差関数を表す。

導出

正規分布確率密度関数\displaystyle y=\frac{1}{\sqrt{2\pi}\sigma} \exp\left(-\frac{(x-\mu)^{2}}{2\sigma^{2}}\right)と表されるので、これを\displaystyle 0から\displaystyle xの範囲で積分し、\displaystyle xを求めることを考える。ところが直接その範囲で積分をすると結果が複雑になるため、この関数が\displaystyle x=\muを軸として線対称なことを利用して、\displaystyle \muから\displaystyle xまでの範囲を求めるものとする。それに伴い、一様乱数\displaystyle A'の範囲も\displaystyle 0 \lt A' \lt 1ではなく\displaystyle -\frac{1}{2} \lt A' \lt \frac{1}{2}を採用する。

\displaystyle \int_\mu^{x}\frac{1}{\sqrt{2\pi}\sigma} \exp\left(-\frac{(t-\mu)^{2}}{2\sigma^{2}}\right) dt=A'

\displaystyle u=\frac{(t-\mu)}{\sqrt{2}\sigma}に置換すると

\displaystyle \frac{1}{\sqrt{\pi}} \int_0^{\frac{(x-\mu)}{\sqrt{2}\sigma}}\exp\left(-u^{2}\right) dt=A'

となる。この積分は初等関数では答えを表せず、誤差関数\displaystyle {\rm erf}(x)を用いて\displaystyle \int \exp\left(-u^{2}\right) dt=\frac{\sqrt{\pi}}{2}{\rm erf}(x)+{\rm C}と表せる。

\displaystyle \frac{1}{\sqrt{\pi}} \frac{\sqrt{\pi}}{2}\left({\rm erf}\left(\frac{(x-\mu)}{\sqrt{2}\sigma}\right)-{\rm erf}(0)\right)=A'

\displaystyle {\rm erf}(0)=0より、

\displaystyle {\rm erf}\left(\frac{(x-\mu)}{\sqrt{2}\sigma}\right)=2A'

ここで、\displaystyle 2A'=A\,(-1\lt A \lt 1)と改め、誤差関数の逆関数を逆誤差関数\displaystyle {\rm erf}^{-1}(A)とすると、

\displaystyle \frac{(x-\mu)}{\sqrt{2}\sigma}={\rm erf}^{-1}(A)

\displaystyle x=\sqrt{2}\sigma\, {\rm erf}^{-1}(A) + \mu

歩数\displaystyle xは整数なので、四捨五入したものを答えとすれば、

\displaystyle x=\left\lfloor \sqrt{2}\sigma\, {\rm erf}^{-1}(A) + \mu +\frac{1}{2} \right\rfloorとして求められる。

 こうして一様乱数からエンカウント歩数を求める式は得られましたが、肝心の逆誤差関数\displaystyle {\rm erf}^{-1}(A)の計算が難しいので、ここまで来たらプログラムに任せましょう。

2000で何とか実装出来ないか試行錯誤したものの実らず、結局は補間により無理やりそれっぽく計算して実装しました。ボクらの故郷というRPG作ってますが、そこではスプライン補間による方法で実装しています。ややこしい割に、その恩恵があまり感じられない気もしますが……