n個の乱数を生成し、それらの平均をとってベルカーブ効果を得るというアイデアを拡張しています。「タイトネス」パラメータは、カーブの勾配を制御します。
編集:「正規」分布を取得するための一連のランダム ポイントの合計は、中心極限定理によってサポートされています。バイアス関数を使用して特定の方向に結果を左右することは一般的な手法ですが、私はその専門家ではありません。
質問の最後にあるメモに対処するために、「内側」の乱数を操作して曲線を歪めています。この例では、指定した指数まで上げています。Random は 1 未満の値を返すため、任意の累乗で 1 を超えることはありません。しかし、1 未満の数の平方、立方体などは底数よりもさらに小さいため、平均はゼロに向かって偏ります。exp = 1 にはスキューがありませんが、exp = 4 にはかなり大きなスキューがあります。
private Random r = new Random();
public double RandomDist(double min, double max, int tightness, double exp)
{
double total = 0.0;
for (int i = 1; i <= tightness; i++)
{
total += Math.Pow(r.NextDouble(), exp);
}
return ((total / tightness) * (max - min)) + min;
}
exp のさまざまな値について試行を実行し、0 から 99 までの 100,000 個の整数を生成しました。分布は次のようになりました。
ピークが exp 値とどのように関連しているかはわかりませんが、exp が高いほど、範囲内に低いピークが現れます。
ループの内側の行を次のように変更して、スキューの方向を逆にすることもできます。
total += (1 - Math.Pow(r.NextDouble(), exp));
...これは、曲線の高い側に偏りを与えます。
編集:では、希望するピークを取得するために「exp」を作成する方法をどのように知るのでしょうか? これは難しい問題であり、おそらく分析的に解決できる可能性がありますが、私は開発者であり、数学者ではありません。そこで、トレードを適用して、多くの試行を実行し、exp のさまざまな値のピーク データを収集し、データをWolfram Alphaの 3 次近似計算機で実行して、exp の方程式をピークの関数として取得しました。
このロジックを実装する新しい関数セットを次に示します。GetExp(...) 関数は、WolframAlpha によって検出された方程式を実装します。
RandomBiasedPow(...) は対象の関数です。指定された範囲内の乱数を返しますが、ピークに向かう傾向があります。その傾向の強さは、タイトネス パラメータによって制御されます。
private Random r = new Random();
public double RandomNormal(double min, double max, int tightness)
{
double total = 0.0;
for (int i = 1; i <= tightness; i++)
{
total += r.NextDouble();
}
return ((total / tightness) * (max - min)) + min;
}
public double RandomNormalDist(double min, double max, int tightness, double exp)
{
double total = 0.0;
for (int i = 1; i <= tightness; i++)
{
total += Math.Pow(r.NextDouble(), exp);
}
return ((total / tightness) * (max - min)) + min;
}
public double RandomBiasedPow(double min, double max, int tightness, double peak)
{
// Calculate skewed normal distribution, skewed by Math.Pow(...), specifiying where in the range the peak is
// NOTE: This peak will yield unreliable results in the top 20% and bottom 20% of the range.
// To peak at extreme ends of the range, consider using a different bias function
double total = 0.0;
double scaledPeak = peak / (max - min) + min;
if (scaledPeak < 0.2 || scaledPeak > 0.8)
{
throw new Exception("Peak cannot be in bottom 20% or top 20% of range.");
}
double exp = GetExp(scaledPeak);
for (int i = 1; i <= tightness; i++)
{
// Bias the random number to one side or another, but keep in the range of 0 - 1
// The exp parameter controls how far to bias the peak from normal distribution
total += BiasPow(r.NextDouble(), exp);
}
return ((total / tightness) * (max - min)) + min;
}
public double GetExp(double peak)
{
// Get the exponent necessary for BiasPow(...) to result in the desired peak
// Based on empirical trials, and curve fit to a cubic equation, using WolframAlpha
return -12.7588 * Math.Pow(peak, 3) + 27.3205 * Math.Pow(peak, 2) - 21.2365 * peak + 6.31735;
}
public double BiasPow(double input, double exp)
{
return Math.Pow(input, exp);
}
これは RandomBiasedPow(0, 100, 5, peak) を使用したヒストグラムで、凡例にさまざまなピーク値が示されています。切り捨てて 0 から 99 の間の整数を取得し、タイトネスを 5 に設定し、20 から 80 の間のピーク値を試しました (極端なピーク値では物事がおかしくなるので、それを省略し、コードに警告を入れました)。あるべき場所にピークが見えます。
次にタイトネスを10まで上げてみた…
分布はよりタイトで、ピークはまだあるべき場所にあります。それもかなり速いです!