確率分布からサンプリングしたいが、正規分布や一様分布のようにライブラリが提供されていない場合にどうするか?逆変換法もその1つですが、棄却サンプリングはより直感的な手法です。
確率分布はを満たすべきだが、正規化定数が不明で、
ののみ既知である場合を考えます。もちろん正規化定数も既知でも問題ないのですが、おそらく不明の場合のほうが一般的だろうと思われます。
全てのに対して
を満たす定数および提案分布
を定めます。視覚的には、提案分布が
を覆っていれば良いという感じですね。この提案分布
からはサンプリングが可能なものを選択します。棄却サンプリングの手順は、
から
をサンプリングする
の確率で
を採択し、採択できなければ再び
から新たなサンプルをとり、採択できるまで繰り返す。
です。手順2によって採択できない、つまり棄却されるサンプル数を少なくするため、提案分布はに近いことが望ましい。また、手順2で確率として解釈できるために、つまり
]の範囲であるために式(2)の条件が必要なんですね。
非常にシンプルでわかりやすい手順です。ああ、こんなことでいいんだって感じ。
さてコードを書いて実験してみました。からサンプリングしてみます。区間は
]とします。わかりやすく提案分布は一様分布としました。つまり
です。
の最大値は1になるので、定数
とします。以下のグラフが結果です。
からうまくサンプリングできていますね。
今回のコードです。なお今回はが対象区間で積分すると1になるようにしましたが、そうでない場合は、このコードのままグラフを描くとサンプリングが失敗しているように見えます。最初はコードにミスがあると思って悩んでしまいました。下のグラフは
とした場合です。
とサンプルのヒストグラムを1つのグラフに描くと形状一致していないように見えてしまいます。
# 棄却サンプリングの実験 import numpy as np import matplotlib.pyplot as plt def p(x): return np.sin(0.5*np.pi*x)**2 # 棄却サンプリング def sampling(): k = 2 # 採択するまでループ while True: # 提案分布q(z)からサンプリング z = np.random.uniform(-1, 1) # [0,kq(z)]の一様分布からサンプリング u = k*np.random.uniform(0, 0.5) # 棄却するか判定 if p(z) > u: return z x = np.linspace(-1, 1, 500) y = p(x) # サンプル数 N = 100000 samples = np.array([sampling() for i in range(N)]) plt.xlim(-1, 1) plt.hist(samples, bins=50,normed=True,alpha=0.5) plt.plot(x, y) plt.show()