6

自分で中央値を定義できるガウス範囲の乱数を作成する乱数ジェネレーターを使用したいと思います。私はすでにここで同様の質問をしました、そして今私はこのコードを使用しています:

class RandomGaussian
    {

        private static Random random = new Random();
        private static bool haveNextNextGaussian;
        private static double nextNextGaussian;

        public static double gaussianInRange(double from, double mean, double to)
        {
            if (!(from < mean && mean < to))
                throw new ArgumentOutOfRangeException();

            int p = Convert.ToInt32(random.NextDouble() * 100);
            double retval;
            if (p < (mean * Math.Abs(from - to)))
            {
                double interval1 = (NextGaussian() * (mean - from));
                retval = from + (float)(interval1);
            }
            else
            {
                double interval2 = (NextGaussian() * (to - mean));
                retval = mean + (float)(interval2);
            }
            while (retval < from || retval > to)
            {
                if (retval < from)
                    retval = (from - retval) + from;
                if (retval > to)
                    retval = to - (retval - to);
            }
            return retval;
        }

        private static double NextGaussian()
        {
            if (haveNextNextGaussian)
            {
                haveNextNextGaussian = false;
                return nextNextGaussian;
            }
            else
            {
                double v1, v2, s;
                do
                {
                    v1 = 2 * random.NextDouble() - 1;
                    v2 = 2 * random.NextDouble() - 1;
                    s = v1 * v1 + v2 * v2;
                } while (s >= 1 || s == 0);
                double multiplier = Math.Sqrt(-2 * Math.Log(s) / s);
                nextNextGaussian = v2 * multiplier;
                haveNextNextGaussian = true;
                return v1 * multiplier;
            }
        }
    }

次に、結果を確認するために、n = 100000000に対してgaussianInRange(0、0.5、1)を使用してプロットしました。 ここに画像の説明を入力してください

ご覧のとおり、中央値は実際には0.5ですが、実際には曲線は表示されていません。だから私は何を間違っているのですか?

編集

私が欲しいのは、値を渡すことで自分で最高の確率を設定できるようなものです。

ここに画像の説明を入力してください

4

2 に答える 2

3

法線偏差を特定の範囲内にあることを条件として描画する最も簡単な方法は、棄却サンプリングを使用することです。

do {
    retval = NextGaussian() * stdev + mean;
} while (retval < from || to < retval);

無条件の通常のジェネレーターで円の中に座標( v1v2 )を描画する場合も、同じようなことが使用されます。

範囲外の値を単純に折りたたむだけでは、同じ分布は生成されません。


また、誤差関数とその逆関数を適切に実装している場合は、逆CDFを使用して値を直接計算できます。正規分布のCDFは

F(retval) = (1 + erf((retval-mean) / (stdev*sqrt(2)))) / 2

打ち切り分布のCDFは

C(retval) = (F(retval) - F(from)) / (F(to) - F(from)), from ≤ x < to

CDFを使用して乱数を描画するにはv、[0、1]の一様分布から描画し、を解きC(retval) = vます。これは与える

double v = random.NextDouble();
double t1 = erf((from - mean) / (stdev*sqrt(2)));
       t2 = erf((to   - mean) / (stdev*sqrt(2)));
double retval = mean + stdev * sqrt(2) * erf_inv(t1*(1-v) + t2*v);

特定のパラメータについて事前t1に計算できます。t2このアプローチの利点は、棄却サンプリングがないため、NextDouble()ドローごとに1つだけ必要なことです。[from、to]の間隔が小さい場合、これは速くなります。


ただし、代わりに二項分布が必要な場合があるようです。

于 2011-03-12T10:59:19.557 に答える
3

グラフジェネレーターにも同様のメソッドがあります(少し変更する必要があります)。

特定の範囲のジェネレーター関数を使用して、ランダムな浮動小数点数を返します。

private double NextFunctional(Func<double, double> func, double from, double to, double height, out double x)
{
    double halfWidth = (to - from) / 2;
    double distance = halfWidth + from;

    x = this.rand.NextDouble() * 2 - 1;// -1 .. 1

    double y = func(x);

    x = halfWidth * x + distance;
    y *= height;

    return y;
}

ガウス関数:

private double Gauss(double x)
{
    // Graph should look better with double-x scale.
    x *= 2;

    double σ = 1 / Math.Sqrt(2 * Math.PI);
    double variance = Math.Pow(σ, 2);
    double exp = -0.5 * Math.Pow(x, 2) / variance;

    double y = 1 / Math.Sqrt(2 * Math.PI * variance) * Math.Pow(Math.E, exp);

    return y;
}

乱数を使用してグラフを生成する方法:

private void PlotGraph(Graphics g, Pen p, double from, double to, double height)
{
    for (int i = 0; i < 1000; i++)
    {
        double x;
        double y = this.NextFunctional(this.Gauss, from, to, height, out x);

        this.DrawPoint(g, p, x, y);
    }
}

私はむしろ余弦関数を使用したいと思います-それはあなたのニーズのためにはるかに速くそしてガウス関数にかなり近いです:

double x;
double y = this.NextFunctional(a => Math.Cos(a * Math.PI), from, to, height, out x);

メソッドのout double xパラメーターNextFunctional()はそこにあるので、グラフで簡単にテストできます(メソッドではイテレーターを使用しています)。

于 2011-03-12T15:40:07.207 に答える