サンプリング分布(sampling distributions)
サンプリング分布(sampling distributions)とは、標本から分布の特徴がわかっている場合に、その特徴を指定することにより、望みの分布を作り出す分布のことだ。
離散分布(std::discrete_distribution<IntType>)
簡単な説明
離散分布(discrete distribution)は整数型の乱数double型で与える。
たとえば、{1.0, 1.0, 1.0}という確率群を渡した場合、離散分布は
もし、{1.0, 2.0, 3.0}という確率群を渡した場合、離散分布は
例えば公平な6面ダイスを作りたい場合、{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}を指定すると+1すると
6の目だけ2倍高い確率で出るイカサマ6面ダイスを作りたい場合、{1.0, 1.0, 1.0, 1.0, 1.0, 2.0}を指定すると、0から4までの5つの目は
{1.0, 1.0, 1.0, 1.0, 1.0, 2.0}
{0.1, 0.1, 0.1, 0.1, 0.1, 0.2}
{2.0, 2.0, 2.0, 2.0, 2.0, 4.0}数学的な説明
std::discrete_distribution<IntType>は整数型の乱数
別に指定のない場合、分布パラメーターは
変数の宣言
std::discrete_distributionの変数を宣言するには3つの方法がある。いずれもdouble型の値をn個渡すための方法だ。
イテレーターのペア
変数の宣言:
std::discrete_distribution<IntType> d( firstW, lastW ) ;IntTypeは整数型でデフォルトはint、[firstW, lastW)はイテレーターのペアで、double型に変換可能な値を参照している。
利用例:
int main()
{
std::array ps = {1.0, 2.0, 3.0} ;
std::discrete_distribution d( std::begin(ps), std::end(ps) );
std::mt19937 e ;
d(e)
}初期化リスト
利用例:
std::discrete_distribution<IntType> d( {...} ) ;
std::discrete_distribution<IntType> d = {...} ;...にはdouble型の浮動小数点数を指定する
利用例:
int main()
{
std::discrete_distribution d( { 1.0, 2.0, 3.0 } );
// もしくは
// ... d = { 1.0, 2.0, 3.0 } ;
std::mt19937 e ;
d(e)
}個数、デルタ、関数
このコンストラクターは以下のように宣言されている。
template<class UnaryOperation>
discrete_distribution(
size_t nw,
double xmin, double xmax,
UnaryOperation fw
);UnaryOperationは1つの実引数を取る関数オブジェクトで戻り値の型はdouble型に変換できること。さらに、double型はUnaryOperationの引数に変換可能なこと。もし
もしfwはn回を超えて呼ばれることはない。
int main()
{
std::discrete_distribution d( 5, 0.0, 1.0, [](auto x){
std::cout << x << '\n' ;
if ( x < 0.3 )
x = 0.3 ;
if ( x > 0.8 )
x = 0.8 ;
return x ;
} );
}このdは、
std::discrete_distribution d = {0.3, 0.3, 0.5, 0.7, 0.8 } ;と初期化されたものと同じように初期化される。
初期化パラメーターの確認
std::discrete_distributionの内部状態はメンバー関数probabilitiesで取得できる。戻り値の型はstd::vector<double>で、指定した確率群が要素になっている。
int main()
{
std::discrete_distribution d = { 1.0, 2.0, 3.0 } ;
auto v = d.probabilities() ;
// vは{1.0, 2.0, 3.0}
}応用例
以下は6の目が2倍の確率で出るイカサマ6面ダイスの実装だ。
template < typename Engine >
int roll_dice( Engine & e )
{
std::discrete_distribution d = { 1.0, 1.0, 1.0, 1.0, 1.0, 2.0 } ;
return d(e) + 1 ;
}区分定数分布(std::piecewise_constant_distribution<RealType>)
簡単な説明
区分定数分布(piecewise constant distribution)とは、区分と、区分ごとの確率を指定し、いずれかの区分の範囲の値に一様分布させる分布だ。ここでいう確率は、密度、あるいはウエイトともいう。
1つの区分はdouble型の値2つ
例えば{0.0, 1.0}という2つのdouble型の値を使って1つの区分を与えた場合、これは{0.0, 1.0, 2.0}という3つのdouble型の値は2つの区分になり、それぞれ
一般に、double型の値double型の値は、後続の値より小さくなければならないということだ。
以下は正しい区分の指定だ。
{1.0, 2.0, 100.0, 999.999}
{-1.0, 1.0, 2.0}
{-5.0, -4.0, -3.1}以下は正しくない区分の指定だ。
{1.0, 0.0}これは
それぞれの区分double型で指定する。
例えば{0.0, 1.0}という1つの区分と{1.0}という1つの確率を与えた場合、
{0.0, 1.0, 10.0}という2つの区分と、{1.0, 2.0}という2つの確率を与えた場合、
数学的な説明
std::piecewise_constant_distribution<RealType>は浮動小数点数型の乱数
この分布の区間境界(interval boundaries)ともいう
一般にウエイト(weight)と呼ばれている値
変数の宣言
std::piecewise_constant_distributionでは、double型の値の集合を2つ渡す必要がある。1つは区間を指定するためのdouble型に変換可能な値で、もう1つは区間ごとの確率を指定するためのdouble型に変換可能な値だ。
イテレーターによる指定
イテレーターで区間と確率を指定するコンストラクターは以下のとおり。
template<class InputIteratorB, class InputIteratorW>
piecewise_constant_distribution(
InputIteratorB firstB, InputIteratorB lastB,
InputIteratorW firstW
);[firstB, lastB)は区間を指定するためのfirstWはそれぞれの区間の確率を指定するlastWがないのは、確率の個数は
もし[firstB, lastB)のサイズが1以下の場合、区間は[0.0, 1.0)になり、確率は
利用例:
int main()
{
std::array bs = {-1.0, 1.0, 2.0 } ;
std::array ps = { 1.0, 5.0 } ;
std::piecewise_constant_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
std::mt19937 e ;
d(e) ;
}bsは区間を指定する値の集合、psは区間ごとの確率だ。
区間は[-1.0, 1.0)と[1.0, 2.0)の2つ。確率はそれぞれ
区間を表現する値が足りない場合は以下のとおり。
int main()
{
// 区間を指定すべき値が足りない
std::array bs = { 1.0 } ;
std::array ps = { 1.0, 5.0 } ;
// 引数は無視される。
// 区間は[0.0, 1.0), 確率は100%
std::piecewise_constant_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
}初期化リストと関数オブジェクトによる指定
初期化リストと関数を指定するコンストラクターは以下のとおり。
template<class UnaryOperation>
piecewise_constant_distribution(
initializer_list<RealType> bl,
UnaryOperation fw
);イテレーターのペアと同じく、区間は[bl.begin(), bl.end())で指定する。
確率は
bl.size()が1以下の場合、区間は[0.0, 1.0)になり、確率は
利用例:
int main()
{
std::piecewise_constant_distribution d(
{1.0, 2.0, 3.0, 4.0, 5.0},
[]( auto x )
{ return x ; }
) ;
}この場合、区間は[1.0, 2.0), [2.0, 3.0), [3.0, 4.0), [4.0, 5.0)の4個になり、確率は{1.5, 2.5, 3.5, 4.5}となる。
区間数、最小、最大、関数オブジェクトによる指定
コンストラクターの宣言:
template<class UnaryOperation>
piecewise_constant_distribution(
size_t nw,
RealType xmin, RealType xmax,
UnaryOperation fw
);nwは区間数、xminは最小値、xmaxは最大値、fwは関数オブジェクトで、double型から変換できる型の実引数を取り、double型に変換可能な戻り値を返す。
$ k = 0, \dotsc, n - 1
利用例:
int main()
{
std::piecewise_constant_distribution d( 5, 1.0, 5.0,
[]( auto x ) { return x ; } ) ;
}この場合、区間の集合は{1.0, 1.8, 2.6, 3.4, 4.2, 5.0}となり、確率は{1.4, 2.2, 3.0, 3.8, 4.6}となる。
内部状態の取得
std::piecewise_constant_distributionの内部状態は、メンバー関数intervalsとdensitiesで得ることができる。
template<class RealType = double>
class piecewise_constant_distribution {
public :
vector<result_type> intervals() const;
vector<result_type> densities() const;
} ;intervalsは区間、densitiesは確率を返す。
int main()
{
auto bs = { 1.0, 2.0, 3.0 } ;
auto ps = { 1.0, 2.0 } ;
std::piecewise_constant_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
// {1.0, 2.0, 3.0}
auto intervals = d.intervals() ;
// {0.333333, 0.666667}
auto densities = d.densities() ;
}densities()の結果が正規化されているのは、ユーザーが指定した確率は
区分線形分布(std::piecewise_linear_distribution<RealType>)
簡単な説明
区分線形分布(piecewise linear distribution)は区分定数分布と同じく、区間と確率(またの名を密度、ウエイト)を指定する。
区間の指定は区分定数分布と同じだ。内部境界の集合で指定する。例えば{1.0, 2.0, 3.0}は2つの区間[1.0, 2.0)と[2.0, 3.0)を指定する。
区分線形分布における確率は、区間に対してではなく、内部境界に対して指定する。指定した全区間における値の出現確率は、内部境界から内部境界に向かって指定した確率の差の方向に線形に増加、もしくは減少する。
例えば区分{0.0, 1.0}と確率{1.0, 2.0}を指定した場合、これは1つの区間[0.0, 1.0)について、内部境界0.0の確率は1.0の確率は\frac{2}{3}とし、xを生成する。内部境界区間の範囲に注意。1.0未満なので、1.0は出ない。
そして、区間の間の値は、区間を区切る2つの内部境界の確率の差によって、線形に増加、もしくは減少する。例えば値0.25が出る確率は0.5が出る確率は1.75が出る確率は
区分{0.0, 1.0, 2.0}と確率{1.0, 2.0, 1.0}の場合、2つの区間[0.0, 1.0)と[1.0, 2.0)の範囲について、0.0から1.0に向かう区間についての確率は1.0から2.0に向かう区間についての確率は
結果として、乱数値の分布をグラフに描画すると、1.0が最も出やすく、その前後±1.0の範囲で徐々に減少していく山のようなグラフになる。
TODO: グラフ、横軸が乱数値、縦軸が確率
* + \frac{1}{2}
*** |
***** |
******* |
********* |
*********** + \frac{1}{4}
***********
***********
***********
***********
-+----+----+
0.0 1.0 2.0fig/fig39-01.png
数学的な説明
std::piecewise_linear_distribution<RealType>は乱数
一般に内部境界とも呼ばれる
変数の宣言
piecewise_linear_distributionは区間と確率を指定するためにn個のdouble型に変換可能な値を指定する必要がある。
イテレーターによる指定
template<class InputIteratorB, class InputIteratorW>
piecewise_linear_distribution(
InputIteratorB firstB, InputIteratorB lastB,
InputIteratorW firstW );[firstB, lastB)は区間、firstWから区間数までのイテレーターが確率。
firstB == lastBもしくは++firstB == lastBの場合、つまり内部境界が1個以下で、空の場合、区間数は1つで[0.0, 1.0)の範囲、確率は{0.0, 1.0}となる。
使い方:
int main()
{
auto bs = { 0.0, 1.0, 2.0 } ;
auto ps = { 1.0, 2.0, 1.0 } ;
std::piecewise_linear_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
std::mt19937 e ;
d(e) ;
}空の場合。
int main()
{
auto bs = { 0.0 } ;
auto ps = { 0.0 } ;
std::piecewise_linear_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
}これは以下のコードと同じだ。
int main()
{
auto bs = { 0.0, 1.0 } ;
auto ps = { 0.0, 1.0 } ;
std::piecewise_linear_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
}初期化リストと関数オブジェクトによる指定
template<class UnaryOperation>
piecewise_linear_distribution(
initializer_list<RealType> bl,
UnaryOperation fw
);区間を指定する内部境界は[bl.begin(), bl.end())、内部境界
内部境界が1個以下の場合はイテレーターの場合と同じ。
使い方:
int main()
{
std::piecewise_linear_distribution d(
{0.0, 1.0, 2.0},
[](auto x){ return x ; }
) ;
}これは以下のコード同じだ。
int main()
{
auto bs = { 0.0, 1.0, 2.0 } ;
auto ps = { 0.0, 1.0, 2.0 } ;
std::piecewise_linear_distribution d( std::begin(bs), std::end(bs), std::begin(ps) ) ;
}個数、最小値、最大値、関数オブジェクトによる指定
template<class UnaryOperation>
piecewise_linear_distribution(
size_t nw,
RealType xmin, RealType xmax,
UnaryOperation fw
);nwが個数、xminが最小値、xmaxが最大値、fwが関数オブジェクト。
関数オブジェクトfwはdouble型から変換できる実引数を1つだけ取り、戻り値の型はdouble型に変換できること。
関係
内部境界
使い方:
int main()
{
std::piecewise_linear_distribution d(
5,
1.0, 5.0,
[](auto x){ return x ;}
) ;
}上のコードは以下のコードと同じだ。
int main()
{
auto params = { 1.8, 2.6, 3.4, 4.2, 5.0, 5.8 } ;
std::piecewise_linear_distribution d( std::begin(params), std::end(params), std::begin(params) ) ;
}