整数範囲から通常分布したランダムを生成する方法は?
-
19-09-2019 - |
質問
整数範囲の開始と終了を考えると、この範囲の間に正規分布したランダム整数を計算するにはどうすればよいですか?
正規分布が - +無限に入ることを理解しています。テールはカットオフになる可能性があると思うので、ランダムが範囲外で計算されると、再計算されます。これにより、範囲内の整数の確率が向上しますが、この効果が許容できる限り(<5%)、問題ありません。
public class Gaussian
{
private static bool uselast = true;
private static double next_gaussian = 0.0;
private static Random random = new Random();
public static double BoxMuller()
{
if (uselast)
{
uselast = false;
return next_gaussian;
}
else
{
double v1, v2, s;
do
{
v1 = 2.0 * random.NextDouble() - 1.0;
v2 = 2.0 * random.NextDouble() - 1.0;
s = v1 * v1 + v2 * v2;
} while (s >= 1.0 || s == 0);
s = System.Math.Sqrt((-2.0 * System.Math.Log(s)) / s);
next_gaussian = v2 * s;
uselast = true;
return v1 * s;
}
}
public static double BoxMuller(double mean, double standard_deviation)
{
return mean + BoxMuller() * standard_deviation;
}
public static int Next(int min, int max)
{
return (int)BoxMuller(min + (max - min) / 2.0, 1.0);
}
}
標準偏差を範囲と比較して標準偏差をスケーリングする必要がありますが、方法がわかりません。
答え:
// Will approximitely give a random gaussian integer between min and max so that min and max are at
// 3.5 deviations from the mean (half-way of min and max).
public static int Next(int min, int max)
{
double deviations = 3.5;
int r;
while ((r = (int)BoxMuller(min + (max - min) / 2.0, (max - min) / 2.0 / deviations)) > max || r < min)
{
}
return r;
}
解決
Box-Mullerメソッドが「標準」正規分布を返す場合、平均0と標準偏差1を持つ場合、標準正規分布を変換するには、乱数をxを掛けて標準偏差xを取得し、yを追加して取得します。 y、メモリが私に正しく役立つ場合。
を参照してください 標準の通常の変数の正規化に関するウィキペディア記事のセクション(プロパティ1) より正式な証拠のために。
あなたのコメントに応えて、経験則は、正規分布の99.7%が標準偏差の+/- 3倍以内になるということです。たとえば、0から100までの正規分布が必要な場合、平均が中途半端になり、SDは(100/2)/3 = 16.667になります。したがって、Box-Mullerアルゴリズムから得られる値は何でも、16.667を掛けて分布を「ストレッチ」し、50を「中央」に追加します。
ジョン、あなたの最新のコメントに応えて、私は本当に何がポイントなのか本当にわかりません Next
関数。常に1の標準偏差と、最小と最大の間の平均の平均を使用します。
yの平均が必要な場合は、範囲-x〜 +xの数値の約99.7%が BoxMuller(Y, X/3)
.
他のヒント
さて、-2*sigma ..+2*sigmaは、ベル曲線の95%を与えます。 (すでに述べたWikiの記事で「標準偏差と信頼区間」セクションを確認してください)。
したがって、この作品を変更してください。
return (int)BoxMuller(min + (max - min) / 2.0, 1.0);
1.0(標準偏差)を2.0に変更します(または95%以上のカバレッジが必要な場合はさらに多く)
return (int)BoxMuller(min + (max - min) / 2.0, 2.0);