銀河セクターで手続き的に惑星を生成するための数列を生成したいと思います。各惑星はランダムに配置する必要がありますが、2つの惑星が互いに直接隣接していることはほとんどありません。どうすればそれを達成できますか?
分布関数を適用することで可能性を変更できることを知っていますが、特定の値をより多く/より少なくするためにそれらをどのように制御できますか?
銀河セクターで手続き的に惑星を生成するための数列を生成したいと思います。各惑星はランダムに配置する必要がありますが、2つの惑星が互いに直接隣接していることはほとんどありません。どうすればそれを達成できますか?
分布関数を適用することで可能性を変更できることを知っていますが、特定の値をより多く/より少なくするためにそれらをどのように制御できますか?
回答:
必要な分布がわかっている場合は、拒否サンプリングを使用できます。
最も簡単な方法:上のグラフで、曲線の下に点が見つかるまでランダムに点を選択します。次に、x
-座標を使用します。
実際の配布については、さまざまなアプローチが考えられます。たとえばi
、場所の惑星番号p
といくつかの強度パラメータk
(たとえば0.5
)の場合、関数を定義してf_i(x)=abs(p-x)^k
から、分布関数を使用しますg(x)=f_1(x)*f_2(x)*...*f_n(x)
。
実際にはg(x)
、配列t
(t[x]=g(x)
)の結果を計算して保存します。最高値h
も覚えてください。でランダムな位置x
をt
選択し、〜のy
間0
でランダムな値を選択してh
、繰り返しif y>t[x]
ます。それ以外の場合、戻り値はx
です。
問題が質問によって完全に特定されているかどうかはわかりませんが、いくつかの簡単なアイデアを提供できます。これらの2番目は、あなたの写真があなたが望んでいることを大まかに示す数字を提供します。
分布関数が生成されるたびに変化し、メモリがある(つまり、非マルコビアンである)ことを理解できるどちらの方法でも、「メモリ」(以前に描画された数値の数)が取得されたときに、これらの方法のいずれかが実用的でない可能性があります。非常に大きい。
シンプル:
平坦な分布から乱数を生成し、以前に描画したnnumbersと比較し、「近すぎる」場合は繰り返す
この答えはあなたの図に似ています(0..1から描画したい場合):
エンドポイントビンは特殊なケースですが、それらの処理方法を確認するのに十分なほど単純である必要があります。
1つのサイコロと3つのサイコロの違いを考えてみてください。1ダイスはすべての値の確率を均等にしますが、3ダイスは中央の値の確率が高くなる傾向があります。
方程式の「ダイス」が多ければ多いほど、中心に向かって何かを得る可能性が高くなります。したがって、任意の数を均等に処理できる関数を定義してみましょう。
// Takes a random number between floor and ceil
// pow defines how strongly these results should gravitate towards the middle
// We also define a function TrueRand(floor, ceil) elsewhere where you should substitute your own random function
int CenterRandom(int floor, int ceil, int pow = 3)
{
if(ceil == floor)
return ceil; // don't care to compare
int total = 0;
for(int x = 0; x < pow; x++)
{
total += TrueRand(floor, ceil);
}
return total / pow;
}
これを使用するサンプル関数を定義できます。
// Distribues a number of points between floor and ceil
// We assume a function PlotPoint(int) exists to aid in creating the planet, etc...
void DistributePoints(int floor, int ceil, int numPoints)
{
// Could easily output this in the function parameters, but language wasn't specified
int[numPoints] breaks;
int numBreaks = 0;
// Special case for first pair
breaks[0] = CenterRandom(floor, ceil);
numBreaks++;
for(int x = 0; x < numPoints - 1; x++)
{
// Generate a random number linearly, this will be used for picking
// This way we have a greater chance of choosing a random value between larger pairs
int picker = TrueRandom(floor, ceil);
// Now we first find the pair of points that our picker exists on
// For simplicity, we handle the first and last pair separately
if(picker >= floor && picker < breaks[0])
{
breaks[x] = CenterRandom(floor, breaks[0] - 1);
}
for(int i = 0; i < numBreaks; i++)
{
if(picker > breaks[i] && picker < breaks[i+1])
{
breaks[x] = CenterRandom(breaks[i] + 1, breaks[i+1] - 1);
}
}
if(picker > breaks[numBreaks] && picker <= ceil)
{
breaks[x] = CenterRandom(breaks[numBreaks] + 1, ceil);
}
PlotPoint(breaks[x]); // Plot the point
}
}
最初に注意する点は、このコードはピッカーがすでにポイントの1つと一致するかどうかを実際にはチェックしないことです。もしそうなら、それはポイントを生成するつもりはないでしょう、おそらくあなたが望むかもしれない何か。
ここで何が起こっているのかを説明すると、CenterRandomは一種のベルカーブを生成します。この関数は、平面を複数のベルカーブに分割します。存在するポイントのペアごとに1つです。ピッカーは、どのベルカーブから生成するかを指示します。線形に選択するため、それらの間に大きなギャップがあるペアがより頻繁に選択されるようにすることができますが、それでも完全にランダムのままにします。
これがあなたを正しい方向に向けることを願っています。
ランダムな位置のシーケンスについて質問しているのは知っていますが、セットを順番に生成することに制限されていない場合は、別のアプローチがあります。それは、目的の間隔を持つポイントのセットを生成することです。
あなたが欲しいと思うのは、ある程度のランダムさで適度に間隔を空けた惑星のセットです。乱数ジェネレータで惑星の位置を生成する代わりに、乱数ジェネレータで惑星の間隔を生成します。これにより、その分布から選択する乱数ジェネレータを使用して、間隔の分布を直接制御できます。これは1次元では簡単です。
2次元では、「ブルーノイズ」を生成するいくつかのアプローチを見てきましたが、任意の分布で間隔を生成する方法がわかりません。この記事では、標準の「配置してみて、近すぎる場合は拒否する」という標準的なアプローチについて説明しますが、すべてのポイントを配置し、ロイドリラクゼーションを使用してすべての惑星をより多くの場所に移動することで、「ソフトな」ソリューションで一度に生成できます。望ましい位置。近すぎる惑星を遠ざけます。再帰的なWang Tilesは、便利な別のアプローチです。この紙問題を拡張して、ある密度の惑星と、別の密度の小惑星のような他のオブジェクトを生成します。また、フーリエ級数を使用してブルーノイズを生成できる場合もあります。よく分かりません。フーリエ級数アプローチでは、ブルーノイズだけでなく、任意の分布を使用することもできます。