Comment puis-je créer un générateur «aléatoire» qui est biaisé par des événements antérieurs?


37

Je cherche à mettre en place un système basé sur le hasard qui est biaisé par un événement antérieur.

Contexte: Il y a quelques années, je me souviens d'une mise à jour de World of Warcraft annonçant la mise en place d'un nouveau calculateur de chances qui contrecarrerait les chaînes d'événements épineuses. (par exemple faire des coups critiques ou esquiver plusieurs fois de suite). L'idée était que, dans l'éventualité où vous éviteriez un coup, les chances d'esquiver le prochain coup seraient réduites, mais cela fonctionnerait dans les deux sens. Ne pas éviter un coup augmenterait également les chances d'esquiver le prochain coup. L'astuce majeure ici était que, sur plusieurs essais, la chance d'esquiver correspondrait toujours au pourcentage donné au joueur dans sa feuille de statistiques.

Ce type de système m'intriguait beaucoup à l'époque et je suis maintenant dans la situation d'avoir besoin d'une telle solution.

Voici mes ennuis:

  • J'imagine que je serais capable de trouver des ressources en ligne sur la mise en œuvre d'un tel système, mais il me manque peut-être juste les mots à la mode pertinents pour le trouver.
  • J'ai également besoin de cette approche pour s'adapter à un système qui n'est pas binomial (c'est-à-dire deux résultats), mais qui contient plutôt quatre événements mutuellement exclusifs.

Mon approche actuelle est similaire à celle d'un système de ticket de tombola. Lorsqu'un événement se produit, je modifie les poids en faveur de tous les autres événements. Cela pourrait fonctionner si les quatre événements étaient censés être tout aussi probables, mais dans mon cas, le sur doit être beaucoup plus répandu. Mais comme l'événement principal se produit plus souvent, il déplace les poids de l'autre beaucoup plus haut que prévu et je n'arrive pas à trouver les chiffres pour les déplacements de poids nécessaires pour conserver le nombre moyen de tickets autour des valeurs initiales de l'événement. donné.

Quelques indications de direction ou un exemple clair seraient très appréciés.


4
Si vous voulez une réponse très nuancée ou sophistiquée, vous aurez peut-être plus de chance de demander à Mathematics.SE. Les mathématiciens sont à l'aise pour répondre à des questions complexes sur les probabilités. math.stackexchange.com
Kevin - Rétablir Monica


6
Programmers.SE est une alternative au site de mathématiques où vous seriez plus susceptible de comprendre les réponses . La conception des algorithmes ne concerne pas particulièrement les mathématiques et vous auriez probablement besoin de concevoir une conception initiale pour obtenir des informations utiles.
Lilienthal

1
Je conviens avec Kevin et Lilienthal que vous pourriez obtenir une meilleure réponse, mais en lisant la réponse de mklingen, j’ai réalisé que ce qui est décrit ici peut être modélisé comme une chaîne de Markov et qu’il pourrait être un outil pratique pour les développeurs de jeux. Je vais essayer d'écrire cela plus en détail plus tard.
Nwellcome

1
Comme je coche les chiffres sur certaines des réponses ici, je découvre qu'il existe un certain nombre de contraintes différentes, et qu'une solution qui les résoudrait toutes pourrait être plus complexe que ce dont vous avez besoin. Certaines informations plus spécifiques sur votre cas d'utilisation pourraient vous aider à préciser les meilleures options. Par exemple, les probabilités de vos événements sont-elles assez similaires (par exemple, 5 résultats différents avec 20% de chances chacune) ou très différentes (par exemple, 10% ratent 80% et 10% critiques)? Souhaitez-vous minimiser les analyses (par exemple, 3 échecs consécutifs) ou les blocages / attentes (par exemple, 3 échecs sur 8 essais, ou 20 essais avant que je ne devienne critique)?
DMGregory

Réponses:


19

En gros, vous demandez un générateur d'événements "semi-aléatoire" qui génère des événements avec les propriétés suivantes:

  1. Le taux moyen auquel chaque événement se produit est spécifié à l'avance.

  2. Le même événement est moins susceptible de se produire deux fois de suite qu’il ne le serait au hasard.

  3. Les événements ne sont pas totalement prévisibles.

Une solution consiste à implémenter d’abord un générateur d’événements non aléatoires répondant aux objectifs 1 et 2, puis à ajouter de l’aléatoire à l’objectif 3.


Pour le générateur d'événements non aléatoires, nous pouvons utiliser un algorithme de dithering simple . Plus précisément, prenons p 1 , p 2 , ..., p n les vraisemblances relatives des événements 1 à n , et s = p 1 + p 2 + ... + p n la somme des poids. Nous pouvons ensuite générer une séquence d'événements non aléatoires au maximum équidistribuée au maximum en utilisant l'algorithme suivant:

  1. Initialement, posons e 1 = e 2 = ... = e n = 0.

  2. Pour générer un événement, incrémentez chaque e i de p i et indiquez l’événement k pour lequel e k est le plus grand (rompre les liens à votre guise).

  3. Décrémentez e k de s et répétez à partir de l’étape 2.

Par exemple, étant donné trois événements A, B et C, avec p A = 5, p B = 4 et p C = 1, cet algorithme génère quelque chose comme la séquence de sorties suivante:

A B A B C A B A B A A B A B C A B A B A A B A B C A B A B A

Remarquez comment cette séquence de 30 événements contient exactement 15 As, 12 Bs et 3 Cs. Ce n'est pas tout à fait la distribution optimale - il y a quelques occurrences de deux As consécutives, ce qui aurait pu être évité - mais cela s'en rapproche.


Maintenant, pour ajouter un caractère aléatoire à cette séquence, vous avez plusieurs options (qui ne s’excluent pas nécessairement):

  • Vous pouvez suivre les conseils de Philipp et conserver une "suite" de N événements à venir, pour un nombre N de taille appropriée . Chaque fois que vous devez générer un événement, vous choisissez un événement aléatoire dans la console, puis vous le remplacez par l'événement suivant généré par l'algorithme de dithering ci-dessus.

    En appliquant ceci à l'exemple ci-dessus, avec N = 3, on obtient par exemple:

    A B A B C A B B A B A B C A A A A B B A B A C A B A B A B A

    alors que N = 10 donne le résultat le plus aléatoire:

    A A B A C A A B B B A A A A A A C B A B A A B A C A C B B B

    Notez que les événements communs A et B se retrouvent avec beaucoup plus de courses en raison du brassage, alors que les rares événements C sont encore assez bien espacés.

  • Vous pouvez injecter un caractère aléatoire directement dans l'algorithme de dithering. Par exemple, au lieu d’incrémenter e i de p i à l’étape 2, vous pouvez l’incrémenter de p i × random (0, 2), où random ( a , b ) est un nombre aléatoire uniformément réparti entre a et b ; cela donnerait une sortie comme celle-ci:

    A B B C A B A A B A A B A B A A B A A A B C A B A B A C A B

    ou vous pouvez incrémenter e i de p i + aléatoire (- c , c ), ce qui produirait (pour c = 0,1 × s ):

    B A A B C A B A B A B A B A C A B A B A B A A B C A B A B A

    ou, pour c = 0,5 × s :

    B A B A B A C A B A B A A C B C A A B C B A B B A B A B C A

    Notez comment le schéma additif a un effet de randomisation beaucoup plus fort pour les événements rares C que pour les événements communs A et B, par rapport au processus multiplicatif; cela pourrait ou non être souhaitable. Bien sûr, vous pouvez également utiliser une combinaison de ces schémas, ou tout autre ajustement des incréments, tant que cela préserve la propriété que l' incrément moyen de e i est égal à p i .

  • Vous pouvez également perturber la sortie de l'algorithme de dithering en remplaçant parfois l'événement k choisi par un événement aléatoire (choisi en fonction des poids bruts p i ). Tant que vous utilisez également le même k à l'étape 3 que celui que vous avez généré à l'étape 2, le processus de dithering aura toujours tendance à égaliser les fluctuations aléatoires.

    Par exemple, voici un exemple de sortie avec 10% de chances que chaque événement soit choisi au hasard:

    B A C A B A B A C B A A B B A B A B A B C B A B A B C A B A

    et voici un exemple avec 50% de chances que chaque sortie soit aléatoire:

    C B A B A C A B B B A A B A A A A A B B A C C A B B A B B C

    Vous pouvez aussi envisager de nourrir un mélange d'événements purement aléatoires et tramées dans une plate - forme / piscine mélange, comme décrit ci - dessus, ou randomiser peut - être l'algorithme de tramage en choisissant k au hasard, pesée par les e i s (traitement de poids négatifs zéro).

Ps. Voici quelques séquences d'événements complètement aléatoires, avec les mêmes taux moyens, à des fins de comparaison:

A C A A C A B B A A A A B B C B A B B A B A B A A A A A A A
B C B A B C B A A B C A B A B C B A B A A A A B B B B B B B
C A A B A A B B C B B B A B A B A A B A A B A B A C A A B A

Tangent: Puisqu'il y a eu un débat dans les commentaires sur la nécessité, pour les solutions basées sur le pont, de permettre au pont de se vider avant de le remplir, j'ai décidé de faire une comparaison graphique de plusieurs stratégies de remplissage du pont:

Plot
Tracé de plusieurs stratégies pour générer des lancers de pièces semi-aléatoires (avec un rapport 50:50 des têtes aux queues en moyenne). L’axe horizontal correspond au nombre de retournements, l’axe vertical correspond à la distance cumulée par rapport au rapport attendu, mesurée comme suit: (têtes - queues) / 2 = têtes - retournements / 2.

Les lignes rouges et vertes du graphique montrent deux algorithmes non basés sur le pont pour la comparaison:

  • Ligne rouge, tramage déterministe : les résultats pairs sont toujours des têtes, les résultats impairs sont toujours des queues.
  • Ligne verte, retournements aléatoires indépendants : chaque résultat est choisi indépendamment au hasard, avec une probabilité de tête de 50% et une chance de queue de 50%.

Les trois autres lignes (bleu, violet et cyan) illustrent les résultats de trois stratégies basées sur les decks, chacune mise en œuvre à l'aide d'un deck de 40 cartes, qui est initialement rempli de 20 cartes "têtes" et de 20 cartes "queues":

  • Ligne bleue, remplir à vide : les cartes sont tirées au hasard jusqu'à ce que le paquet soit vide, puis le paquet est rempli de 20 cartes "têtes" et de 20 cartes "queues".
  • Ligne violette, à remplir à moitié vide : les cartes sont tirées au sort jusqu'à ce qu'il reste 20 cartes dans le paquet; le paquet est ensuite complété avec 10 cartes "têtes" et 10 cartes "queues".
  • Ligne cyan, à remplir en permanence : les cartes sont tirées au hasard; les tirages pairs sont immédiatement remplacés par une carte "têtes", et les tirages impairs avec une carte "queues".

Bien entendu, l’intrigue ci-dessus n’est qu’une simple réalisation d’un processus aléatoire, mais elle est raisonnablement représentative. En particulier, vous pouvez voir que tous les processus basés sur le deck ont ​​un biais limité et restent assez proches de la ligne rouge (déterministe), alors que la ligne verte purement aléatoire finit par s’égarer.

(En fait, la différence entre les lignes bleue, violette et cyan par rapport à zéro est strictement délimitée par la taille du pont: la ligne bleue ne peut jamais dériver plus de 10 pas de zéro, la ligne violette ne peut en avoir que 15. et la ligne cyan peut dériver au maximum de 20 pas de zéro.Bien sûr, aucune ligne atteignant réellement sa limite est extrêmement improbable, car elle a tendance à se rapprocher de zéro si elle s’éloigne trop de.)

En un coup d'œil, il n'y a pas de différence évidente entre les différentes stratégies basées sur le pont (bien que, en moyenne, la ligne bleue reste un peu plus proche de la ligne rouge et que la ligne cyan reste un peu plus éloignée), mais un examen plus attentif de la ligne bleue révèle un motif déterministe distinct: tous les 40 tirages (marqués par les lignes verticales grises en pointillés), la ligne bleue correspond exactement à la ligne rouge à zéro. Les lignes violettes et cyan ne sont pas aussi strictement contraintes et peuvent rester à l’écart de zéro à tout moment.

Pour toutes les stratégies basées sur les decks, la caractéristique importante qui garde leur variation limitée est le fait que, bien que les cartes soient tirées du deck au hasard, le deck est rempli de manière déterministe. Si les cartes utilisées pour remplir le paquet étaient elles-mêmes choisies au hasard, toutes les stratégies basées sur le paquet deviendraient impossibles à distinguer du choix au hasard (ligne verte).


Réponse très élaborée. L'ajout de facteurs aléatoires à l'algorithme de dithering semble simple. :)
Sonate

Décidé d'aller avec votre réponse. :) Mais je vous recommanderais de placer les ajouts de la vue d'ensemble de la méthode en haut. Ce que je vais faire, sur la base de votre réponse, est d’essayer à la fois les solutions "rouge" et "violette".
Sonaten

53

Ne lancez pas de dés, distribuez des cartes.

Prenez tous les résultats possibles de votre RNG, mettez-les dans une liste, mélangez-les au hasard et renvoyez les résultats dans l'ordre aléatoire. Lorsque vous êtes à la fin de la liste, répétez.

Les résultats seront toujours distribués uniformément, mais les résultats individuels ne seront pas répétés à moins que le dernier de la liste ne soit également le premier de la suivante.

Lorsque cela est un peu trop prévisible à votre goût, vous pouvez utiliser une liste nindiquant le nombre de résultats possibles et multiplier par chacun les résultats possibles navant de les mélanger. Ou vous pouvez remanier la liste avant qu’elle soit complètement itérée.


1
recherche "shuffle bag" (sur ce site même)
jhocking

3
C’est la raison pour laquelle de nombreux jeux Tetris évitent de laisser le joueur affamé pour des pièces clefs trop longtemps. Il est important de vider le sac / le paquet comme le suggère Philipp avant d’insérer de nouvelles cartes si vous souhaitez contrôler les occurrences sur un intervalle défini. En réinsérant les cartes au fur et à mesure (ou en réajustant les poids), vous pouvez fausser la distribution de probabilité de manière difficile à calculer et à se tromper.
DMGregory

2
@DMGregory: En fait, il est tout à fait judicieux de mélanger de nouvelles cartes avant de vider le jeu de cartes (et, en fait, je le recommanderais pour rendre les résultats plus naturels et plus difficiles à prédire). L'important est de faire en sorte que la fraction (moyenne) de nouvelles cartes mélangées dans la plate - forme est égale à la fraction désirée que vous voulez dessiner sur de celui - ci.
Ilmari Karonen

4
Illmari Karonen: lorsque vous remplacez des éléments, vous pouvez perdre les avantages du sac de lecture aléatoire en termes de limitation des résultats identiques ou de longs écarts entre des résultats particuliers. Si votre taux de remplacement est égal à la distribution de probabilité cible, vous êtes maintenant dans la même position que de générer chaque résultat indépendamment de manière aléatoire. Si elle n'est pas égale à la distribution de probabilité cible, vous pouvez alors fausser les probabilités effectives de manière difficile à prévoir et à équilibrer en conséquence - le demandeur décrit la difficulté à résoudre exactement ce problème.
DMGregory

2
D'accord avec @DMGregory. En mélangeant de nouvelles cartes, vous invalidez le système même. Le système de distribution de cartes est spécifiquement et parfaitement adapté au résultat souhaité. Par exemple, lorsque vous retirez une reine (pour utiliser des cartes traditionnelles, par exemple) du jeu, la probabilité de tirer une reine diminue et la probabilité de tirer une carte autre qu'une reine augmente. C'est un système d'auto-ajustement, si vous voulez.
Volte

17

Vous pouvez essayer un graphe aléatoire de Markov . Considérez chaque événement pouvant survenir comme un nœud d’un graphique. À partir de chaque événement, créez un lien vers un autre événement qui pourrait éventuellement suivre. Chacun de ces liens est pondéré par quelque chose appelé la probabilité de transition . Ensuite, vous effectuez une marche aléatoire du graphique en fonction du modèle de transition.

Par exemple, vous pouvez avoir un graphique qui représente le résultat d'une attaque (coup critique, esquive, etc.). Initialisez le nœud de départ à celui choisi au hasard en fonction des statistiques du joueur (lancez simplement les dés). Ensuite, lors de la prochaine attaque, décidez de ce qui se passera par la suite, en fonction du modèle de transition.

Il faut prendre soin de décider comment pondérer les transitions. D'une part, toutes les transitions en sortie d'un nœud doivent être additionnées jusqu'à une probabilité de 1. Une chose simple à faire est d'effectuer une transition de chaque nœud à chaque autre nœud, avec des pondérations équivalentes à la probabilité que ces événements se produisent. a priori , étant donné que l'événement en cours ne peut plus se reproduire.

Par exemple, si vous avez trois événements:

  Critical, P = 0.1
  Hit,      P = 0.3
  Miss,     P = 0.6

Vous pouvez configurer le modèle de transition de sorte qu'un coup critique ne se produise plus en redistribuant uniformément sa masse de probabilité aux autres événements:

  Critical -> Critical,   P = 0.0
  Critical -> Hit,        P = 0.35
  Critical -> Miss,       P = 0.65

EDIT: Comme le disent les commentaires ci-dessous, ce modèle n’est pas assez compliqué pour obtenir le comportement souhaité. Au lieu de cela, vous devrez peut-être ajouter plusieurs états supplémentaires!


1
Le schéma de repondération que vous proposez ne conserve pas les probabilités souhaitées pour chaque état. En effectuant un test empirique avec ces chiffres, les échecs surviennent environ 41% du temps et les critiques environ 25%, ce qui est très différent des valeurs d'entrée. La transition vers les États restants proportionnelle à leurs probabilités (par exemple, Miss a 25% de chances d’aller en Crit et 75% d’avoir un impact) est légèrement meilleure, avec un taux de ratés de 44% et 17% de critiques, mais elle reste faible. ne reflète pas les probabilités souhaitées dans l'entrée.
DMGregory

J'ai oublié la règle de bayes :( Je recalculerai plus tard. Il pourrait ne pas être possible de maintenir la distribution de probabilité a priori car le modèle de transition actuel exclut des séquences possibles telles que CCHM ou CHHM, ou très probablement MMHM, etc.
mklingen

La contrainte "pas de répétition" pourrait vous lier les mains ici, en ce qui concerne les poids extrêmement élevés et faibles. Si vous souhaitez que 1 tentative sur 10 soit critique, la seule façon de procéder de cette méthode consiste à alterner 5 tentatives ratées et 5 touches, ce qui déforme les probabilités de hit et de pertes vers leur moyenne. Aucune séquence sans omissions consécutives ne peut satisfaire les exigences de l'entrée ici.
DMGregory

4
@mklingen, je suis d'accord avec DMGregory, le "strictement pas de répétition" n'est pas souhaitable ici. Au contraire, ils veulent que la probabilité de longues chaînes du même résultat soit moins probable qu'avec une probabilité aléatoire uniforme. Vous pouvez le faire avec une chaîne de Markov (qui est dirigée) qui ressemble à ceci . Cela utilise plusieurs états pour représenter des événements répétés où les probabilités de passer de "Hit 1" à "Hit 2" et de "Hit 2" à "Hit 3+" diminuent et les probabilités de revenir à "Hit 1" et à "Crit 1 "monte.
Nwellcome

@ bienvenu c'est une excellente idée.
Mklingen

3

Voici une implémentation que j'ai créée en C # et qui va:

  • Activer des événements en fonction de probabilités
  • Ajustez ces probabilités pour réduire les risques d'événements récurrents
  • Ne pas s'éloigner trop des probabilités d'origine

J'ai ajouté quelques commentaires pour que vous puissiez voir ce que je fais.

    int percentageEvent1 = 15; //These are the starter values. So given a scenario, the
    int percentageEvent2 = 40; //player would have around a 15 percent chance of event
    int percentageEvent3 = 10; //one occuring, a 40 percent chance of event two occuring
    int percentageEvent4 = 35; //10 percent for event three, and 35 percent for event four.

    private void ResetValues()
    {
        percentageEvent1 = 15;
        percentageEvent2 = 40;
        percentageEvent3 = 10;
        percentageEvent4 = 35;
    }

    int resetCount = 0; //Reset the probabilities every so often so that they don't stray too far.

    int variability = 1; //This influences how much the chance of an event will increase or decrease
                           //based off of past events.

    Random RandomNumberGenerator = new Random();

    private void Activate() //When this is called, an "Event" will be activated based off of current probability.
    {
        int[] percent = new int[100];
        for (int i = 0; i < 100; i++) //Generate an array of 100 items, and select a random event from it.
        {
            if (i < percentageEvent1)
            {
                percent[i] = 1; //Event 1
            }
            else if (i < percentageEvent1 + percentageEvent2)
            {
                percent[i] = 2; //Event 2
            }
            else if (i < percentageEvent1 + percentageEvent2 + percentageEvent3)
            {
                percent[i] = 3; //Event 3
            }
            else
            {
                percent[i] = 4; //Event 4
            }
        }
        int SelectEvent = percent[RandomNumberGenerator.Next(0, 100)]; //Select a random event based on current probability.

        if (SelectEvent == 1)
        {
            if (!(percentageEvent1 - (3 * variability) < 1)) //Make sure that no matter what, probability for a certain event
            {                                                //does not go below one percent.
                percentageEvent1 -= 3 * variability;
                percentageEvent2 += variability;
                percentageEvent3 += variability;
                percentageEvent4 += variability;
            }
        }
        else if (SelectEvent == 2)
        {
            if (!(percentageEvent2 - (3 * variability) < 1))
            {
                percentageEvent2 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent3 += variability;
                percentageEvent4 += variability;
            }
        }
        else if (SelectEvent == 3)
        {
            if (!(percentageEvent3 - (3 * variability) < 1))
            {
                percentageEvent3 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent2 += variability;
                percentageEvent4 += variability;
            }
        }
        else
        {
            if (!(percentageEvent4 - (3 * variability) < 1))
            {
                percentageEvent4 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent2 += variability;
                percentageEvent3 += variability;
            }
        }

        resetCount++;
        if (resetCount == 10)
        {
            resetCount = 0;
            ResetValues();
        }

        RunEvent(SelectEvent); //Run the event that was selected.
    }

J'espère que cela vous aidera, veuillez suggérer des améliorations à ce code dans les commentaires, merci!


1
Ce système de repondération a tendance à rendre les événements équiprobables. Réinitialiser les poids périodiquement n'est en réalité qu'un pansement qui limite la gravité de la situation, tout en garantissant qu'un rouleau sur 10 ne tire aucun avantage de la repondération. En outre, une remarque sur l’algorithme: vous perdez beaucoup de travail en remplissant un tableau de 100 entrées afin de faire votre sélection aléatoire. Au lieu de cela, vous pouvez générer un résultat aléatoire, puis parcourir vos 4 résultats, en additionnant leurs probabilités au fur et à mesure. Dès que le résultat est inférieur à la somme, vous avez votre résultat. Aucun remplissage de table requis.
DMGregory

3

Permettez-moi de généraliser un peu la réponse de mklingen . Fondamentalement, vous voulez implémenter Fallacy du Gambler , bien que je vais fournir une méthode plus générale ici:

Supposons qu'il existe ndes événements possibles avec des probabilités p_1, p_2, ..., p_n. Quand un événement se iproduit, sa probabilité doit être redimensionnée avec un facteur 0≤a_i≤1/p_i(ce dernier est important, sinon vous vous retrouvez avec une probabilité supérieure à un et les autres événements doivent avoir des probabilités négatives , ce qui signifie fondamentalement des " anti " événements. Ou quelque chose), bien que typiquement a_i<1. Vous pouvez par exemple choisir a_i=p_i, ce qui signifie que la probabilité qu'un événement se produise une seconde fois est la probabilité initiale que l'événement se produise précisément deux fois de suite, par exemple, un second tirage au sort aurait une probabilité de 1/4 au lieu de 1/2. D'autre part, vous pouvez également en avoir a_i>1, ce qui signifierait déclencher un "coup de chance / malheur".

Tous les autres événements doivent rester également probables les uns par rapport aux autres, c'est-à-dire qu'ils doivent tous être redimensionnés avec le même facteur, de b_isorte que la somme de toutes les probabilités soit égale à un, c'est-à-dire

1 = a_i*p_i + b_i*(1-p_i)  # Σ_{j≠i) p_j  = 1 - p_i
 b_i = (1 - a_i*p_i) / (1 - p_i).   (1)

Jusqu'ici, si simple. Mais ajoutons maintenant une autre exigence: compte tenu de toutes les séquences possibles de deux événements, les probabilités à événement unique extraites de celle-ci doivent être les probabilités d’origine.

Laisser

        / p_i * b_i * p_j  (ji)
p_ij = <
        \ a_i * (p_i     (j=i)

Indiquez la probabilité que l'événement se jproduise après l'événement iet notez que p_ij≠p_jisauf si b_i=b_j (2)(ce qui (1)implique a_j = 1 - a_i*p_i + (1-a_i)*p_i/p_j). C’est aussi ce que le théorème de Bayes exige et cela implique aussi

Σ_j p_ij = p_i * b_i * (1 - p_i) + a_i * (p_i
         = b_i * p_i + (a_i - b_i) * (p_i
         = p_i  # using (1)

juste comme désiré. Notez simplement que cela signifie que l’on a_icorrige tous les autres.


Voyons maintenant ce qui se passe lorsque nous appliquons cette procédure plusieurs fois, c'est-à-dire pour des séquences de trois événements ou plus. Il existe essentiellement deux options pour le choix des probabilités truquées du troisième événement:

a) Oubliez le premier événement et installez-vous comme si seulement le second se produisait, c'est-à-dire

         / p_ij * a_j * p_j  (j=k)
p_ijk = <
         \ p_ij * b_j * p_l  (jk)

Notez que cela viole généralement Bayes, car par exemple p_jik≠p_ikjdans la plupart des cas.

b) Utilisez les probabilités p_ij(pour fixe i) comme nouvelles probabilités à pi_jpartir desquelles vous obtenez les nouvelles probabilités pi_jkpour que l'événement kse produise ensuite. Que vous le modifiiez ai_jou non dépend de vous, mais sachez que les nouveaux bi_jsont définitivement différents en raison de la modification pi_j. Là encore, le choix de ai_jest probablement limité en exigeant que toutes les permutations ijkse produisent avec la même probabilité. Voyons voir...

         / p_ij * bi_j * pi_k  (jk)
p_ijk = <
         \ (p_ij * ai_j      (j=k)

         / b_i * bi_j * p_i * p_j * pi_k  (ijki)
         | b_i * ai_j * p_i * (p_j      (ij=k)
      = <  a_i * (p_i * bi_i * pi_k     (i=jk)
         | b_i * p_i * bi_j * p_k * pi_i  (i=kj)
         \ a_i * ai_i * (p_i * pi_i     (i=k=j)

et leurs permutations cycliques, qui doivent être égales pour les cas respectifs.

J'ai bien peur que ma suite sur ce sujet devra attendre un moment ...


En testant cela de manière empirique, il en résulte toujours une distorsion par rapport aux probabilités d'entrée sur plusieurs exécutions. Si a_i / p_i = 0,5, par exemple (et en utilisant les chiffres de la réponse de mklingen), un taux d'entrée manquant de 60% devient un taux observé de 50,1% et un taux critique d'entrée de 10%, de 13,8%. Vous pouvez le vérifier en portant la matrice de transition résultante à une puissance élevée. Le choix de rapports a_i: p_i plus proches de 1 entraîne moins de distorsion, mais également une efficacité moindre en termes de réduction des cycles.
DMGregory

@DMGregory bon point: vous ne pouvez pas simplement prendre les pouvoirs de la matrice de transition. Je développerai ma réponse plus tard
Tobias Kienzler le

@DMGregory J'ai commencé à décrire le processus complet (variante b)), mais cela devient assez fastidieux et je manque de temps: /
Tobias Kienzler le

1

Je pense que la meilleure option consiste à utiliser une sélection d’éléments pondérée de manière aléatoire. Il existe une implémentation pour C # ici , mais ils peuvent être facilement trouvés ou créés pour d’autres langues.

L'idée serait de réduire le poids d'une option à chaque fois qu'il est sélectionné et de l'augmenter chaque fois qu'il n'est pas sélectionné.

Par exemple, si vous diminuez le poids de l'option choisie NumOptions-1et que vous augmentez le poids de toutes les autres options de 1 (en prenant soin de supprimer les éléments de poids <0 et de les relire lorsqu'ils dépassent 0) , chaque option sera sélectionnée approximativement. le même nombre de fois sur une longue période, mais les options récemment choisies seront beaucoup moins susceptibles d'être choisies.


Le problème avec l'utilisation d'un ordre aléatoire, comme suggéré par de nombreuses autres réponses, est qu'après chaque option choisie, vous pouvez prédire avec une certitude à 100% quelle option sera sélectionnée ensuite. Ce n'est pas très aléatoire.


1

Ma réponse est incorrecte, mon test était imparfait.

Je laisse cette réponse ici pour la discussion et les commentaires qui soulignent les failles de cette conception, mais le test réel était incorrect.

Ce que vous recherchez, c'est un poids pondéré: les poids de vos quatre résultats possibles doivent être encore ajustés (pondérés) par les résultats précédents, tout en conservant les poids appropriés.

Le moyen le plus simple d’y parvenir est de modifier tous les poids de chaque rouleau en diminuant le poids correspondant à la valeur lancée et en augmentant les autres .

Par exemple, disons que vous avez 4 poids: Fumble, Miss, Hit et Crit. Disons également que votre poids total souhaité pour eux est Fumble = 10%, Miss = 50%, Hit = 30% et Crit = 10%.

Si vous utilisez un générateur de nombres aléatoires (RNG) pour produire des valeurs comprises entre 1 et 100, puis comparez cette valeur à celle où elle se situe dans cette plage (1-10 Fumble, 11-60 ratés, 61-90 hits, 91-100 critiques ), vous générez un rouleau individuel.

Si, lorsque vous effectuez ce jet, vous ajustez immédiatement ces plages en fonction de la valeur obtenue, vous pondérerez les futurs jetons, mais vous devrez également réduire le poids du résultat roulé du même montant total que celui auquel vous augmentez les autres poids. Ainsi, dans notre exemple ci-dessus, vous réduiriez le poids laminé de 3 et augmenteriez les autres poids de 1 chacun.

Si vous faites cela pour chaque lancer, vous aurez toujours la possibilité de stries, mais elles seront considérablement réduites, car vous augmentez les chances que les futurs jets soient différents du résultat actuel. Vous pouvez augmenter cet effet, et ainsi réduire davantage le risque de traînées, en augmentant / diminuant les poids d'un facteur plus important (par exemple, réduisez le courant de 6 et augmentez les autres de 2).

J'ai lancé une application rapide pour valider cette approche et, après 32 000 itérations avec ces poids, les graphiques suivants sont générés. Le graphique supérieur montre les 4 valeurs de pondération immédiates à chaque rouleau, et le graphique inférieur affiche le nombre total de chaque type de résultat obtenu jusqu'à ce point.

Comme vous pouvez le constater, les poids fluctuent légèrement autour des valeurs souhaitées, mais les poids globaux restent dans les plages souhaitées et, une fois que la variété initiale des nombres de départ a été réglée, les résultats correspondent presque parfaitement aux pourcentages souhaités.

Notez que cet exemple a été produit à l'aide de la classe .NET System.Random, qui n'est pas vraiment l'un des meilleurs RNG, vous pouvez donc probablement obtenir des résultats plus précis en utilisant un meilleur RNG. Notez également que 32 000 était le maximum de résultats que je pouvais représenter graphiquement avec cet outil, mais mon outil de test a été capable de générer plus de 500 millions de résultats avec les mêmes modèles globaux.


Notez que cela ne fonctionne que si vos + 1s / -3 sont appliqués par rapport aux poids d'origine, plutôt qu'aux poids les plus récemment utilisés. (En modifiant continuellement les poids de manière uniforme, cela les fait dériver pour qu'ils soient équiprobables). Bien que cela maintienne la probabilité sur la cible à long terme, cela ne fait que très peu pour réduire les exécutions. Étant donné que j'ai raté une fois, la probabilité que je manque deux fois de suite est de 22% avec ce régime, contre 25% avec les tirages indépendants. Augmenter le décalage de poids pour un effet plus important (par exemple à + 3 / -9) a pour effet de biaiser la probabilité à long terme.
DMGregory

En réalité, les données présentées ci-dessus appliquent le + 1 / -3 au dernier poids chaque fois qu'un rouleau est traité. Donc, si vous ratez une fois le poids initial de 50%, le prochain poids manquant serait de 47%, et si vous ratez encore une fois, le poids suivant serait de 44%, et ainsi de suite. Il réduit les suites (une métrique distincte était les pistes de suivi, jusqu'à une réduction de 24%), mais elles sont toujours inévitables car ce schéma a toujours de fortes chances de laisser chacune des 4 pondérations avec une probabilité non nulle ( Par exemple, quatre coups dans une rangée laisseraient le poids critique avec zéro chance de se produire).
David C Ellis

Si telle était votre intention, alors votre implémentation a un bogue. Regardez le graphique - Le poids de fumble ne rebondit jamais qu'entre 7 et 11, sans aucune valeur en dehors de cela. J'ai exécuté une simulation en utilisant la modification continue que vous décrivez, et les graphiques sont radicalement différents, les probabilités de chaque état convergeant vers 25% chacune au cours des cent premiers essais.
DMGregory

Dangit, en effet il a été buggé comme tu l'as fait remarquer. Eh bien, frappe cette réponse.
David C Ellis

@DavidCEllis êtes-vous en train de dire que votre implémentation était défectueuse ou que l'idée elle-même l'est? Mon intuition au dos d'une serviette est venue à peu près au modèle que vous décrivez (ajustez une probabilité vers le bas une fois tirée, restaurez progressivement toutes les probabilités à leurs valeurs d'origine au fil du temps) et cela a toujours un sens pour moi.
Dimo414

0

Vous pouvez faire ce qui est essentiellement un filtre. Gardez une trace des n événements passés. La probabilité est le filtre parmi certains appliqué à ces événements. Le filtre 0 est la probabilité de base, si 0 alors vous avez esquivé, si 1 vous avez échoué. Supposons que la base était de 25% et que le filtre diminue de moitié à chaque itération. Votre filtre serait alors:

[.25 .125 .0625 .03125] 

N'hésitez pas à continuer si vous le souhaitez. La probabilité globale de ce schéma est légèrement supérieure à la probabilité de base de 0,25. En fait, la probabilité, étant donné le même schéma, est (j'appelle x la probabilité réelle, p est l'entrée de probabilité):

x=p+(1-x)*(p/2+p/4+p/8)

La résolution de x, on trouve la réponse est p(1+1/2+1/4+1/8)/(1+p(1/2+1/4+1/8), ou pour notre cas donné, x=0.38461538461. Mais ce que vous voulez vraiment, c'est trouver p, étant donné x. Cela s'avère être un problème plus difficile. Si vous supposez un filtre infini, le problème devient x+x*p=2*p, oup=x/(2-x) . Donc, en augmentant votre filtre, vous pourriez alors résoudre un nombre p qui vous donnera en moyenne les mêmes résultats, mais à un taux dépendant des succès récents.

Fondamentalement, vous utilisez les valeurs précédentes pour déterminer le seuil d'acceptation de cette ronde et prenez une valeur aléatoire. Produisez ensuite la valeur aléatoire suivante en fonction du filtre.


-1

Tout comme vous l’avez proposé, l’une des solutions consiste à mettre en œuvre un système aléatoire pondéré. L'idée est de créer un générateur de nombres aléatoires (ou de résultats) où les pondérations et les résultats peuvent être modifiés.

Voici une implémentation de ceci en Java.

import java.util.Map;
import java.util.Random;

/**
 * A psuedorandom weighted outcome generator
 * @param <E> object type to return
 */
public class WeightedRandom<E> {

    private Random random;
    private Map<E, Double> weights;

    public WeightedRandom(Map<E, Double> weights) {
        this.random = new Random();
        this.weights = weights;
    }

    /**
     * Returns a random outcome based on the weight of the outcomes
     * @return
     */
    public E nextOutcome() {
        double totalweight = 0;

        // determine the total weigth
        for (double w : weights.values()) totalweight += w;

        // determine a value between 0.0 and the total weight
        double remaining = random.nextDouble() * totalweight;

        for (E entry : weights.keySet()) {
            // subtract the weight of this entry
            remaining -= weights.get(entry);

            // if the remaining is smaller than 0, return this entry
            if (remaining <= 0) return entry;
        }

        return null;
    }

    /**
     * Returns the weight of an outcome
     * @param outcome the outcome to query
     * @return the weight of the outcome, if it exists
     */
    public double getWeight(E outcome) {
        return weights.get(outcome);
    }

    /**
     * Sets the weight of an outcome
     * @param outcome the outcome to change
     * @param weight the new weigth
     */
    public void setWeight(E outcome, double weight) {
        weights.put(outcome, weight);
    }
}

EDIT Dans le cas où vous souhaitez ajuster les poids automatiquement, par exemple, augmentez les chances de A lorsque le résultat est B. Vous pouvez soit,

  1. Changer le comportement du nextOutcome() méthode, de sorte qu'il modifie le poids en fonction du résultat
  2. Utilisez setWeight()pour modifier le poids de en fonction du résultat.

Je pense que vous avez mal compris la question: le PO ne demande pas comment générer des résultats aléatoires pondérés, mais comment ajuster les poids afin de réduire la probabilité que le même résultat se produise plusieurs fois de suite.
Ilmari Karonen

Je vois, j'ai changé une partie de ma réponse pour expliquer comment cela serait possible en utilisant ce système.
erikgaal
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.