ランダム分布関数を変更する::シーケンスで複数の類似した値を取得する可能性を低くする


8

銀河セクターで手続き的に惑星を生成するための数列を生成したいと思います。各惑星はランダムに配置する必要がありますが、2つの惑星が互いに直接隣接していることはほとんどありません。どうすればそれを達成できますか?

分布関数を適用することで可能性を変更できることを知っていますが、特定の値をより多く/より少なくするためにそれらをどのように制御できますか?

すでに生成された値に応じて確率曲線を変更するアイデアを示すGIF。


2
最小距離を追加するだけで、惑星が隣接していないことを確認できます。これは簡単だと思いますので、もう少し詳しく説明してもらえますか?
Madmenyo 2014

@MennoGouwはい、これはこの特定のケースではそれを解決しますが、確率の理解を深めたいので、生成された数値をハード制限/破棄せずに「よりソフトな」ソリューションを探しています。
API-Beast

「よりソフトな」ソリューションを明確にします。ルールの設定がすべてです。手続き型の生成に特定のルールが必要な場合は、これらのルールを追加する必要があります。特別なケースがある場合は、これらのルールにさらに多くのまたは異なるルールを設定します。
Madmenyo 2014

ディストリビューションについて高い評価を得ているジェネレーターを使用しないのはなぜですか?(メルセンヌツイスターは悪くないと思います。)
Vaillancourt

2
同意する。ランダム生成自体は問題ではありません。これを行うと、ランダムジェネレーターが予測可能になり、壊れることがあります。ルールの生成は、進むべき道です。
ashes999

回答:


8

必要な分布がわかっている場合は、拒否サンプリングを使用できます。

最も簡単な方法:上のグラフで、曲線の下に点が見つかるまでランダムに点を選択します。次に、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)、配列tt[x]=g(x))の結果を計算して保存します。最高値hも覚えてください。でランダムな位置xt選択し、〜のy0でランダムな値を選択してh、繰り返しif y>t[x]ます。それ以外の場合、戻り値はxです。


分布関数の定義についてもう少し詳しく教えてください。残りはかなり明確でなければなりません。
API-Beast

たとえば、現在の惑星が0.1、0.3、0.8の位置にある場合、g(x)=(abs(x-0.1)* abs(x-0.3)* abs(x-0.8))^ 0.5、ここで「^」累乗を意味します。(これは前の式とは少し異なりますが、同等です。)この分布関数は、質問のgifのように見え、特に何にも基づいていません。(WolframAlphaのクエリ文字列: "0から1までのプロット(abs(x-0.1)* abs(x-0.3)* abs(x-0.8))^ 0.5")
yarr

うわー、その機能はかなりすごいです。そのような関数が実際には単純であることを知りませんでした:)レイジーのリンク: bit.ly/1pWOZMJ
API-Beast

1

問題が質問によって完全に特定されているかどうかはわかりませんが、いくつかの簡単なアイデアを提供できます。これらの2番目は、あなたの写真があなたが望んでいることを大まかに示す数字を提供します。

分布関数が生成されるたびに変化し、メモリがある(つまり、非マルコビアンである)ことを理解できるどちらの方法でも、「メモリ」(以前に描画された数値の数)が取得されたときに、これらの方法のいずれかが実用的でない可能性があります。非常に大きい。

  1. シンプル:
    平坦な分布から乱数を生成し、以前に描画したnnumbersと比較し、「近すぎる」場合は繰り返す

  2. この答えはあなたの図に似ています(0..1から描画したい場合):

    • 新しい順序付きリストを作成し、0と1を挿入します
    • フラット分布関数から乱数を生成:N_0
      • この番号をリストに追加します
    • 次の呼び出しで、別の番号N_1を描画します。
    • N_1> N_0の場合
      • mean = 1で希望の標準偏差oの新しいガウス乱数を描画します。小さい数(1-N_1と比較)は、乱数の間隔を広げます。これはドロー間の最小距離を保証するものではありませんが、その場合もあなたの姿はそうではありません。
    • N_1 <N_0の反対の場合は同様に処理されます
    • 後続の描画では、フラット分布から乱数(N_i)を生成し続けます
    • リストをトラバースして、以前に描画された2つの数値のうち、新しい数値が(N_-、N_ +)の間にあるものを確認します
    • 平均(N_- + N _ +)/ 2で新しいガウス乱数を作成する
    • (順序付きリスト)リストにフラット分布番号(N_i)を追加する

エンドポイントビンは特殊なケースですが、それらの処理方法を確認するのに十分なほど単純である必要があります。


0

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つです。ピッカーは、どのベルカーブから生成するかを指示します。線形に選択するため、それらの間に大きなギャップがあるペアがより頻繁に選択されるようにすることができますが、それでも完全にランダムのままにします。

これがあなたを正しい方向に向けることを願っています。


0

ランダムな位置のシーケンスについて質問しているのは知っていますが、セットを順番に生成することに制限されていない場合は、別のアプローチがあります。それは、目的の間隔を持つポイントのセットを生成することです。

あなたが欲しいと思うのは、ある程度のランダムさで適度に間隔を空けた惑星のセットです。乱数ジェネレータで惑星の位置を生成する代わりに、乱数ジェネレータで惑星の間隔を生成します。これにより、その分布から選択する乱数ジェネレータを使用して、間隔の分布を直接制御できます。これは1次元では簡単です。

2次元では、「ブルーノイズ」を生成するいくつかのアプローチを見てきましたが、任意の分布で間隔を生成する方法がわかりません。この記事では、標準の「配置してみて、近すぎる場合は拒否する」という標準的なアプローチについて説明しますが、すべてのポイントを配置し、ロイドリラクゼーションを使用してすべての惑星をより多くの場所に移動することで、「ソフトな」ソリューションで一度に生成できます。望ましい位置。近すぎる惑星を遠ざけます。再帰的なWang Tilesは、便利な別のアプローチです。この紙問題を拡張して、ある密度の惑星と、別の密度の小惑星のような他のオブジェクトを生成します。また、フーリエ級数を使用してブルーノイズを生成できる場合もあります。よく分かりません。フーリエ級数アプローチでは、ブルーノイズだけでなく、任意の分布を使用することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.