Modifier les fonctions de distribution aléatoire :: Rendre moins susceptible d'obtenir plusieurs valeurs similaires dans une séquence


8

Je veux générer une séquence de nombres pour générer des planètes procédurales dans un secteur de galaxie. Chaque planète doit être placée au hasard, mais il est très peu probable que deux planètes soient directement l'une à côté de l'autre. Comment puis-je y parvenir?

Je sais que vous pouvez modifier les chances en appliquant une fonction de distribution, mais comment puis-je les contrôler afin de rendre des valeurs spécifiques plus / moins probables?

Gif montrant l'idée de modifier une courbe de probabilité en fonction des valeurs déjà générées.


2
Le simple fait d'ajouter une distance minimale garantirait qu'une planète n'est pas à côté d'une autre. J'estime que c'est trop simple, pourriez-vous en dire plus?
Madmenyo

@MennoGouw Oui, cela le résoudrait pour ce cas spécifique, bien que je veuille améliorer ma compréhension des probabilités, je cherche donc une solution "plus douce" sans limites strictes / en supprimant les nombres générés.
API-Beast

Clarifiez la solution "plus douce". Il s'agit de fixer des règles. Lorsque vous avez besoin de certaines règles pour la génération de procédures, vous devez ajouter ces règles. Si vous avez des cas particuliers, vous définissez des règles plus ou différentes pour ceux-ci.
Madmenyo

Je ne sais pas pourquoi vous n'utilisez pas seulement un générateur qui a une grande réputation dans sa distribution? (Je pense que le twister de Mersenne n'est pas mauvais.)
Vaillancourt

2
Je suis d'accord. La génération aléatoire elle-même n'est pas le problème. Faire cela peut même casser votre générateur aléatoire en le rendant prévisible. La génération de règles est la voie à suivre.
ashes999

Réponses:


8

Si vous connaissez la distribution souhaitée, vous pouvez utiliser l' échantillonnage de rejet .

Manière la plus simple: dans le graphique ci-dessus, choisissez des points au hasard jusqu'à ce que vous en trouviez un sous la courbe. Ensuite, utilisez simplement la xcoordonnée.

Pour la distribution réelle, il existe différentes approches plausibles. Par exemple, pour le numéro de planète ià l'emplacement pet certains paramètres de force k(par exemple 0.5), définissez une fonction f_i(x)=abs(p-x)^k, puis utilisez la fonction de distribution g(x)=f_1(x)*f_2(x)*...*f_n(x).

En pratique, calculez et stockez les résultats de g(x)to array t( t[x]=g(x)); rappelez-vous hégalement la valeur la plus élevée . Choisissez une position aléatoire xdans t, choisissez une valeur aléatoire yentre 0et h, répétez if y>t[x]; sinon la valeur de retour est x.


Pourriez-vous approfondir un peu la définition de la fonction de distribution? Le reste devrait être assez clair.
API-Beast

Par exemple, si les planètes actuelles sont aux positions 0,1, 0,3 et 0,8, g (x) = (abs (x-0,1) * abs (x-0,3) * abs (x-0,8)) ^ 0,5, où "^" signifie exponentiation. (Ceci est légèrement différent de la formule précédente, mais équivalent.) Cette fonction de distribution ressemble à peu près au gif dans votre question et n'est basée sur rien de particulier. (Chaîne de requête pour WolframAlpha: "tracé de 0 à 1 (abs (x-0,1) * abs (x-0,3) * abs (x-0,8)) ^ 0,5")
yarr

Wow, cette fonction est sacrément cool. Je ne savais pas qu'une fonction comme celle-ci est en fait aussi simple que cela :) Lien pour les paresseux: bit.ly/1pWOZMJ
API-Beast

1

Je ne suis pas sûr que le problème soit complètement spécifié par la question, mais je peux fournir quelques idées simples, la seconde d'entre elles fournira des chiffres à peu près conformes à ce que votre photo indique que vous voulez.

Quoi qu'il en soit, comme vous pouvez le constater, la fonction de distribution change après chaque nombre généré et possède une mémoire (c'est-à-dire: elle n'est pas markovienne ) et l'une ou l'autre de ces méthodes peut s'avérer impraticable lorsque la «mémoire» (nombre de nombres précédemment tirés) devient très grand.

  1. Simple:
    Générez un nombre aléatoire à partir d'une distribution plate, comparez avec les nombres précédemment tirés, répétez si 'trop près'

  2. Cette réponse ressemble plus à votre figure (en supposant que nous voulons tirer de 0..1):

    • créer une nouvelle liste ordonnée, insérer 0 et 1
    • générer un nombre aléatoire à partir d'une fonction de distribution plate: N_0
      • ajouter ce numéro à la liste
    • au prochain appel, tirez un autre numéro N_1,
    • si N_1> N_0
      • dessinez un nouveau nombre aléatoire gaussien avec une moyenne = 1 et un écart-type o de tout ce que vous voulez, un nombre plus petit (par rapport à 1-N_1) gardera les nombres aléatoires plus espacés. Cela ne garantira pas une distance minimale entre les tirages, mais votre figure ne semble pas non plus.
    • cas opposé de N_1 <N_0 traité de la même manière
    • lors des tirages suivants, continuez de générer un nombre aléatoire (N_i) à partir d'une distribution plate
    • parcourez votre liste pour voir de quels deux nombres précédemment tirés le nouveau nombre se situe entre (N_-, N_ +)
    • créer un nouveau nombre aléatoire gaussien avec moyenne (N_- + N _ +) / 2
    • ajoutez le numéro de distribution plat (N_i) à votre liste (liste ordonnée)

Les corbeilles de point de terminaison sont un cas spécial, mais elles devraient être assez simples pour que vous puissiez voir comment les gérer.


0

Pensez à la différence entre 1 dé et 3 dés . 1 dé vous donne une probabilité égale pour toutes les valeurs, tandis que 3 dés auront tendance à avoir une probabilité plus élevée pour les valeurs vers le milieu.

Plus il y a de "dés" dans votre équation, plus vous avez de chances d'obtenir quelque chose vers le centre. Définissons donc une fonction qui peut gérer n'importe quel nombre uniformément :

// 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;
}

Maintenant, nous pouvons définir un exemple de fonction à utiliser:

// 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
    }
}

Maintenant, le premier à noter est que ce code ne vérifie vraiment pas si le sélecteur correspond déjà à l'un des points. Si c'est le cas, cela ne générera tout simplement pas de point, peut-être quelque chose que vous aimeriez.

Pour expliquer ce qui se passe ici, CenterRandom génère une sorte de courbe en cloche. Cette fonction décompose l'avion en plusieurs courbes en cloche, une par paire de points existants. Le sélecteur nous indique à partir de quelle courbe de cloche générer. Étant donné que nous choisissons linéairement, nous pouvons nous assurer que les paires avec des écarts plus importants entre elles seront choisies plus souvent, mais nous laissons tout de même complètement aléatoire.

J'espère que ça vous indique la bonne direction.


0

Je sais que vous posez des questions sur une séquence de positions aléatoires, mais si vous n'êtes pas limité à la génération séquentielle de l'ensemble, il existe une autre approche: générer un ensemble de points qui a l'espacement souhaité.

Ce que je pense que vous voulez, c'est un ensemble de planètes qui sont raisonnablement espacées avec un certain hasard. Au lieu de générer des positions de planète avec un générateur de nombres aléatoires, générez un espacement de planète avec un générateur de nombres aléatoires. Cela vous permettra de contrôler directement la distribution de l'espacement, en utilisant un générateur de nombres aléatoires qui sélectionne cette distribution. C'est simple en 1 dimension.

En 2 dimensions, j'ai vu des approches qui génèrent du «bruit bleu» mais je ne sais pas comment générer un espacement avec une distribution arbitraire. Cet article couvre l'approche standard «essayez de le placer et de le rejeter s'il est trop proche», mais vous pouvez les générer tous en même temps, avec une solution «plus douce» en plaçant tous vos points, puis en utilisant Lloyd Relaxation pour déplacer toutes les planètes vers plus positions souhaitables. Cela éloignera les planètes trop proches. Les tuiles Wang récursives sont une autre approche qui pourrait être utile. Ce papierétend le problème à la génération de planètes avec une densité et d'autres objets comme des astéroïdes avec une autre densité. Vous pourriez également être en mesure de générer du bruit bleu en utilisant la série de Fourier; Je ne suis pas sûr. L'approche de la série de Fourier vous permettrait également d'utiliser des distributions arbitraires au lieu du seul bruit bleu.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.