いくつかの疑似乱数を生成したいのですが、今までは .Net ライブラリのRandom.Next(int min, int max)
機能に非常に満足していました。この種の PRNG は一様分布を使用することになっていますが、指数分布を使用していくつかの数値を生成したいと考えています。
私は C# でプログラミングしていますが、疑似コードや C++、Java なども受け入れます。
提案/コードスニペット/アルゴリズム/考えはありますか?
一様乱数発生器にアクセスできるので、反転法を使用して CDF がわかっている他の分布で分布する乱数を簡単に生成できます。
したがって、 で一様乱数 を生成u
し[0,1)
、次のように計算x
します。
x = log(1-u)/(-λ)
、
ここλ
で、 は指数分布のレート パラメーターです。さて、x
は指数分布の乱数です。log
上記はln
、自然対数であることに注意してください。
サンプリングの基本定理によれば、目的の分布を正規化、統合、反転できれば、ホーム フリーになります。
F(x)
で正規化された目的の分布がある場合[a,b]
。あなたが計算します
C(y) = \int_a^y F(x) dx
それを反転して取得し、 [0,1) に一様にC^{-1}
投げて見つけます。z
x_i = C^{-1}(z_i)
これにより、目的の分布が得られます。
あなたの場合:F(x) = ke^{-kx}
そして私はあなたが望むと仮定します[0,infinity]
. 我々が得る :
C(y) = 1 - e^{-ky}
これは可逆で与えられます
x = -1/k ln(1 - z)
に均一にスローされる z に対して[0,1)
。
しかし、率直に言って、十分にデバッグされたライブラリを使用する方が賢明です。
適切な乱数が必要な場合は、gslルーチンへのリンクを検討してください:http ://www.gnu.org/software/gsl/ 。彼らはルーチンを持っていgsl_ran_exponential
ます。[0、1)に一様分布する組み込みジェネレーターを使用して乱数を生成する場合(たとえば、大きなNの場合はu = Random.Next(0、N-1)/ N)、次を使用します。
-mu * log (1-u)
gslソースのrandist/exponential.cを参照してください。
編集:後のいくつかの答えと比較するためだけに-これはmu = 1/lambdaと同等です。ここでのmuは分布の平均であり、OPがリンクされているウィキペディアページではスケールパラメーターとも呼ばれ、ラムダはレートパラメーターです。
指数分布の 1 つの興味深い特性: 到着間隔が指数関数的である到着プロセスを考えてみましょう。任意の期間 (t1、t2) とその期間の到着を取得します。これらの到着は、t1 と t2 の間で均一に分散されます。(シェルドン・ロス、確率過程)。
疑似乱数ジェネレーターがあり、何らかの理由で (たとえば、ソフトウェアがログを計算できないなど)、上記の変換を実行したくないが、平均が 1.0 の指数 rv が必要な場合。
あなたはできる :
1) 1001 個の U(0,1) 確率変数を作成します。
2) 順番に並べ替える
3) 1 番目から 2 番目を引き、2 番目から 3 番目を引きます... 1000 の差を取得します。
4) これらの差は、平均 = 1.0 の分布からの指数 RV です。
効率は悪いと思いますが、同じ目的のための手段です。
Dan Dyer によるオープンソースのUncommons Maths ライブラリは、Java 用の乱数ジェネレーター、確率分布、組み合わせ論、および統計を提供します。
他の貴重なクラスの中でもExponentialGenerator
、@Alok Singhal によって説明されたアイデアを本質的に実装しています。そのチュートリアル ブログでは、1 分間に平均 10 回発生するランダムなイベントをシミュレートするためのコード スニペットが提供されています。
final long oneMinute = 60000;
Random rng = new MersenneTwisterRNG();
// Generate events at an average rate of 10 per minute.
ExponentialGenerator gen = new ExponentialGenerator(10, rng);
boolean running = true;
while (true)
{
long interval = Math.round(gen.nextValue() * oneMinute);
Thread.sleep(interval);
// Fire event here.
}
もちろん、per second
(ここではなくa minute
) 時間単位を使用したい場合は、 を設定するだけですfinal long oneMinute = 1000
。
のメソッドのソース コードをさらに詳しく調べると、 Generating_exponential_variates [wiki]で説明されている、いわゆる逆変換サンプリングが見つかります。nextValue()
ExponentialGenerator
public Double nextValue()
{
double u;
do
{
// Get a uniformly-distributed random double between
// zero (inclusive) and 1 (exclusive)
u = rng.nextDouble();
} while (u == 0d); // Reject zero, u must be positive for this to work.
return (-Math.log(u)) / rate.nextValue();
}
PS:最近、Uncommons Maths ライブラリを使用しています。ありがとうダン・ダイアー。
私があなたの問題を理解していて、有限数のPRNGを受け入れることができる場合は、次のようなアプローチに従うことができます。
これは、同様の要件に直面したときに私が使用したものです。
// sorry.. pseudocode, mine was in Tcl:
int weighted_random (int max) {
float random_number = rand();
return floor(max - ceil( max * random_number * random_number))
}
もちろん、これは乱数を2乗する式なので、2次曲線に沿って乱数を生成します。