|
何ヶ月ぶりだ……完結編です。 実はすんなり行くだろうと思われていた逆誤差関数の計算がうまくいかなかったので、 長らく放置していたのです。 しかし、エンカウント歩数を計算しなきゃいけない場面に遭遇したので、また考え直しました。 結局、横着してスプライン補間で求めることに。 いやあ、もう本当に、関数が簡単に作れることは大きいことかもしれませんぜ^^; スプライン補間作成機に入れたのは、次のデータです。 y=1000√2 erfinv(0.001x) x y 0 0 50 63 100 126 150 189 200 253 250 319 300 385 350 454 400 524 450 598 500 674 550 755 600 842 650 935 700 1036 750 1150 800 1282 850 1440 900 1645 950 1960 990 2576 995 2807 999 3291 求める式はx=[√2 σerfinv(2A)+μ+1/2]だったのですが、 正規分布グラフをy軸線対称にするため、+1/2は省くことにしました。 ずれ修正のためにあっただけの項なので…… 2Aというのも、ひとまずAに改めて、 x=[√2 erfinv(A)σ+μ] を求める式とします。 ここで、上のデータをもとにスプライン補間を用いて√2 erfinv(A)の計算はできますから、 あとはそれにσを掛けてμ足せば良いだけとなります。 スプライン補間による√2 erfinv(A)の計算のイベント命令は、コモンイベント「逆誤差関数×√2」に入れます。 イベント命令は次のようになります。 ◆変数の操作:[0001:一時変数1]代入,乱数[-999〜999] ◆条件分岐:変数[0001:一時変数1]が0より小さい ◆変数の操作:[0001:一時変数1]乗算,-1 ◆スイッチの操作:[0001:引数が負]をONにする ◆ :それ以外の場合 ◆スイッチの操作:[0001:引数が負]をOFFにする ◆ :分岐終了 ◆イベントの呼び出し:逆誤差関数×√2 ◇変数の操作:[0002:一時変数2]乗算,(10σの値) ◆変数の操作:[0002:一時変数2]除算,1000 ◆変数の操作:[0003:一時変数3]代入,変数[0002]の値 ◆変数の操作:[0003:一時変数3]剰余,10 ◆変数の操作:[0002:一時変数2]除算,10 ◆条件分岐:変数[0003:一時変数3]が5以上 ◆変数の操作:[0002:一時変数2]加算,1 ◆ :分岐終了 ◆条件分岐:スイッチ[0001:引数が負]がON ◇変数の操作:[0002:一時変数2]減算,(μの値) ◆変数の操作:[0002:一時変数2]乗算,-1 ◆ :それ以外の場合 ◇変数の操作:[0002:一時変数2]加算,(μの値) ◆ :分岐終了 ◆ ↑主に◇から始まる行のみ変更します。 変数2番「一時変数2」にエンカウントするまでの歩数が代入されます。 μは平均、σは標準偏差です。求まる歩数は80%の確率でμ±1.2816σの間になります。 例えば、80%の確率で25歩〜35歩になるような場合を考えてみます。 中間の値がμです。μ=(25+35)÷2=30 次に、それぞれの端がμからどれぐらい離れているか見てみると、5歩離れていますね。 つまりこれは、80%の確率で30±5歩になると言い換えられます。 この5を1.2816で割ったものがσです。 よって、σ=5÷1.2816≒3.9 10倍して、10σ≒39となります。 従って、上の◇の行の値はそれぞれ10σの所を39に、μの所を30にすれば完成です。 このイベント命令を実行すると、一時変数2に出力結果が返ります。 μの値である30が最もよく出現し、端になればなるほど出現しづらくなります。 それをグラフにすると、次のような正規分布グラフとなります。 ↑一時変数1が−999から999まで変化したときの一時変数2の値と、その出現回数のグラフです。 30の所がてっぺんになり、25〜35の間が80%を占めています。 出現数の総和は1999なので、17歩や43歩が1回しか出ていないのは 出現確率がそれぞれ1/1999であることを示しています。出ることもあるけれどほぼ出ないということです。 それらの範囲から出ると、もうそのような歩数は絶対に選ばれないことになりますね。 こうすることによって、毎回同じような歩数のときにエンカウントしますが、 まれに早めにエンカウントしたり、長く歩かないとエンカウントしないというようなことが自然に実現できます。 確率分布を変えたいときは、平均μと標準偏差σのみ変えればOKです。 上のイベント命令のμやσの所を変数で設定するようにすれば良いでしょう^^ やっと敵とエンカウントできるようになりました。
ここまで2年掛かってます。作り始めは久我山を作り始めた半年前…… ずいぶんのんびりしておりましたねえ。 |
│└エンカウント確率分布
[ リスト | 詳細 ]
全1ページ
[1]
|
平均30マス、25〜35マスの間で8割方遭遇するといった場合、 平均30マス、標準偏差3.901マスにすればいいのでした。 確率分布から実際に乱数を発生させることにしましょう。 乱数を発生させると、30マスが一番高い確率で選ばれることになります。 RPGツクール2000で発生させられる乱数は一定でしょうから、 乱数の発生だけで直接実現することはできそうにないです。 どうすれば正規分布に従った乱数を発生させられるでしょう。 例えば、分布が上の図のようになっていたらどうでしょうか。 直線になっていて、f(x)=xです。 1〜6までの間ですと、6になる確率が一番高いということになりますね。 確率分布とするならf(x)=x/18としなければなりませんが……簡単のためこうしています。 そして、次のように各整数に対して次のように点を取ります。 x=1のときは点が1個、 x=2のときは点が2個、 x=3のときは点が3個、……としていきます。 これらの点のうちどれが一つを選ぶ確率は、点が21個あるので、1/21です。 そのうち、x=6に対応する点が選ばれる確率は、6/21=2/7となり、最も高いでしょう。 この分布であれば、 21個の点の中から一つを選び、その点がどのxに対応する点なのかを求めれば、 確率分布として使うことができることになります。 x=1の点を1番目、x=2の点を下から2番目、3番目、とし、x=6の一番上の点を21番目として 1〜21の乱数を発生させます。 これで7と出たならば、7番目の点に対応するx=4が選ばれることになります。 こうすれば、x=6が最も出やすく、x=1が最も出にくい確率分布を実現することが出来ます。 これを正規分布に応用してやれば良さそうですね。 しかし、発生させた乱数からどうやって対応するxを求めるのかが問題になります。 上の図であれば、図を見れば対応するxは明らかなのですが、 実際に計算するのはコンピュータです。対応させるための式が必要です。 これは離散値になっていますが、積分すればいいのです。 発生した乱数の番号は、対応するxまでを積分した値として出すことができます。 乱数の番号がA番であったとき、 ∫[0→x]f(t)dt=A が成り立つでしょう、ということです。 f(t)の原始関数をF(t)、その逆関数をFinv(t)とすると、 F(x)−F(0)=A F(x)=A+F(0) x=Finv(A+F(0)) としてxが求められたことになります。 しかしこれでは連続値に対するxであり、更に上の図では赤線が1/2だけずれていますから、 実際のxはこれに1/2を足して小数点以下切り捨てたものになります。 即ち、x=[Finv(A+F(0))+1/2]となります。([]…小数点以下切り捨て) さっきの例で確認してみましょう。 f(x)=xで、この原始関数(積分定数=0)はF(x)=x^2/2となり、 その逆関数(正)は、Finv(x)=√(2x)となります。 よって、 x=[√{2(A+0^2/2)}+1/2] x=[√(2A)+1/2] となります。本当にこれで求められるのかやってみましょう。 7番目の点が選ばれたときx=4に対応していましたが、それを確認してみます。A=7を代入すると、 x=[√(2×7)+1/2] =[√14+1/2] ≒[3.741657+1/2] =[4.241657] =4 確かにx=4と出すことができました。 正規分布の場合はf(x)=1/{√(2π)σ} exp{−(x−μ)^2/(2σ^2)}ですので、 同様にしてF(x)、Finv(x)を求めてやれば利用できます。ちょっと大変そうですが…… 積分定数は0とします。 F(x)=∫f(x)dx =1/{√(2π)σ} ∫exp{−(x−μ)^2/(2σ^2)}dx =1/{√(2π)σ} ∫exp〔−{(x−μ)/(√2 σ)}^2〕dx (x−μ)/(√2 σ)=tとおくと、 dx/(√2 σ)=dt dx=√2 σdt F(x)=1/{√(2π)σ} ∫exp(−t^2)√2 σdt =1/√π ∫exp(−t^2)dt =1/√π √π/2 erf(t) =1/2 erf{(x−μ)/(√2 σ)} これでF(x)が出ました。次はその逆関数です。xについて解きます。 2F(x)=erf{(x−μ)/(√2 σ)} 両辺逆誤差関数(erfinv(x)とします)に入れると、 erfinv(2F(x))=erfinv(erf{(x−μ)/(√2 σ)}) erfinv(2F(x))=(x−μ)/(√2 σ) √2 σerfinv(2F(x))=x−μ x=√2 σerfinv(2F(x))+μ よって、 Finv(x)=√2 σerfinv(2x)+μ となります。 点の位置x=[Finv(A+F(0))+1/2]として求められましたから、それぞれ代入します。 Finv(x)=√2 σerfinv(2x)+μなので、 Finv(A+F(0))=√2 σerfinv(2(A+F(0)))+μ F(x)=1/2 erf{(x−μ)/(√2 σ)}なので、 F(0)=1/2 erf{−μ/(√2 σ)} よって、位置x=[√2 σerfinv(2(A+1/2 erf{−μ/(√2 σ)}))+μ+1/2] 誤差関数は奇関数ですから、 x=[√2 σerfinv(2A−erf{μ/(√2 σ)})+μ+1/2] これをツクール2000で計算すれば、敵遭遇の確率分布が完成したことになります。 しかしとてもじゃないけど大変過ぎます。 そこで、式の簡単化を考えます。 思えばこのxは、 ∫[0→x]f(t)dt=A という式から導き出したのでした。 始点は暗黙の了解で0になっていますが、べつに0じゃくてもいいのです。 正規分布は平均値の所から離れれば離れるほど値が小さくなり、 あっさり目に見えないぐらい小さな値になります。 ツクールで計算するなら、精度の関係でそれは0になるでしょう^^; つまり、↓どこから測ろうと同じになると言えます。 それなら始点をμにして、式が簡単になるように作り変えてしまいましょう。
∫[μ→x]f(t)dt=Aとしてxを求めると、 x=Finv(A+F(μ)) となりますから、 F(μ)=1/2 erf{μ−μ/(√2 σ)}=0より、 x=[√2 σerfinv(2A)+μ+1/2] となります。 これならまだ計算できそうです。 ここで問題になるのは、逆誤差関数の計算です。 初等関数より高度な関数ではありますが、近似式がありますので、 思ったより簡単に近似値を求めることが出来ます。 今度はそれを求めてみましょうね。 |
|
RPGのランダムエンカウントでは、敵と遭遇する場所は確率で決まります。 これは色々な方法で実現できます。 ★ 1マス歩くごとに乱数が発生し、ある確率で敵と遭遇 ★ 敵と遭遇する歩数があらかじめ乱数で決まる ★ フィールド上に見えないイベントがランダムで動いており、触れると遭遇 などがあるでしょう。 1マス歩くごとに乱数が発生する場合は、敵との遭遇確率は小さくしなければなりません。 20歩に1回程度遭遇するのであれば、1/20の確率にすると良いでしょう。 そうすると、敵と遭遇して戦闘が終わった直後、 1/20の確率で1マス歩けばまた遭遇、ということもあり得るでしょう。 それでも良ければ無問題ですが、解決することもできます。 敵と遭遇する歩数がランダムで決まれば、このようなことは起こらなくなります。 例えば、そのマップに来たときに25〜35の乱数を発生させ、 それを敵遭遇までの歩数とすれば良いのです。 そうすると最低25マス歩かなければ敵と遭遇しないので、 上記のように連続遭遇することはなくなります。 その代わり、25〜35の乱数は一定ですので、 24マスまで歩いても絶対遭遇しないのに、25マス以上になると35マス歩くまでに必ず遭遇することになり、 プレイヤーは無意識に歩くマスを数えるようになるかもしれません。 そうすると、無性にストレスを感じることになります。 30マスぐらい歩けば戦闘が起こるようにしたいけれど、 1マスや2マス歩くぐらいでは戦闘は起こらないようにしたいとなれば、どうしましょう。 上記2つの方法では、確率分布が全体的に、又は部分的に一定であることが問題です。 確率分布を一定でなくすれば良いのです。 有名な確率分布として、正規分布というものがあります。 平均μ、標準偏差σとすると、 要素の個数はおおよそ↑このようになります。 正規分布に従っているとすると、要素はμ−σからμ+σの間に全体の約68%があることになっています。 例えばテストの平均点が60点と標準偏差が15点であったとき、 全受験者の68%程度が45点〜75点の間にいるということが分かります。 それ以外の点数であった場合は、残りの3割程度の少数民族だということが分かります。 68%じゃなくて80%にしたいときはどうしたらいいでしょうか。 μ−aσからμ+aσとして、定数aを求めればいいですね。 平均30マス歩けば敵と遭遇、 80%の確率で25〜35マス歩けば敵と遭遇、としたければ、 μ=30、aσ=5としてやればいいわけです。 ではaを求めてみましょうね。 そもそもどうやってμ−σからμ+σの間に全体の約68%がいるか分かったかというと、 この確率分布の面積を求めたのです。 全体(−∞〜∞)の面積と、μ−σ〜μ+σの間の面積との比が約68%になります。 全体の面積というのは実は分かり切っていて、 確率分布なのですから、すべての確率を足し合わせれば100%になります。 なので、全体の面積は1になります。 μ−σ〜μ+σの間の面積を求めてみましょう。 ∫[μ−σ→μ+σ]1/{√(2π)σ} exp{−(x−μ)^2/(2σ^2)}dx =1/{√(2π)σ} ∫[μ−σ→μ+σ] exp{−(x−μ)^2/(2σ^2)}dx =1/{√(2π)σ} ∫[μ−σ→μ+σ] exp〔−{(x−μ)/(√2 σ)}^2〕dx (x−μ)/(√2 σ)=tとおくと、 dx/(√2 σ)=dt dx=√2 σ dt 積分範囲は−1/√2から1/√2になります。 =1/{√(2π)σ} ∫[−1/√2→1/√2] exp(−t^2)√2 σdt =1/√π ∫[−1/√2→1/√2] exp(−t^2)dt e^(−x^2)の積分は初等関数では求められません。 ∫e^(−x^2)dx=√π/2 erf(x)+Cになります。erf(x)は誤差関数といいます。 よって、 =1/√π×√π/2 [erf(t)](範囲:−1/√2→1/√2) =1/2{erf(1/√2)−erf(−1/√2)} 誤差関数は奇関数なので、 =1/2{erf(1/√2)+erf(1/√2)} =erf(1/√2) となります。この値はおよそ0.6827になります。 よって、全体の約68%がその範囲にあることが求められました。 今度はμ−aσからμ+aσまで積分した結果が80%になるときのaを求めます。 ∫[μ−aσ→μ+aσ]1/{√(2π)σ} exp{−(x−μ)^2/(2σ^2)} =erf(a/√2) になり、これが80%になるので、 erf(a/√2)=80% 誤差関数の逆関数を逆誤差関数erfinv(x)とすると、 a/√2=erfinv(80%) a=√2 erfinv(0.8) と求まります。 erfinv(0.8)≒0.9062なので、 a≒√2×0.9062≒1.2816となります。 逆誤差関数の値は、誤差関数が求められればニュートン法を用いて簡単に出せます。 誤差関数は色々な求め方があるようです。 一般に、割合Aにしたいときは a=√2 erfinv(A)として求めることができます。 ずいぶん上でaσ=5としましたから、 a=1.2816より、 σ=5/1.2816≒3.901となります。 よって、25〜35マスの間で80%の確率で遭遇する場合は
平均30マス、標準偏差3.901マスということになります。 これで確率分布のグラフが描けるようになりました。今度は実際に使ってみます。 |
全1ページ
[1]





