Come posso creare un generatore "casuale" che è influenzato da eventi precedenti?


37

Sto cercando di implementare un sistema basato sul caso che è influenzato da un evento precedente.

Sfondo: alcuni anni fa, ricordo un aggiornamento per World of Warcraft che annunciava l'implementazione di un nuovo calcolatore di probabilità che avrebbe contrastato catene appuntite di eventi. (ad es. effettuare attacchi critici o schivare più volte di seguito). L'idea era che nel caso in cui si schivasse un colpo, la possibilità di schivare il colpo successivo sarebbe diminuita, ma avrebbe funzionato in entrambi i modi. Non schivare un colpo aumenterebbe ugualmente la possibilità di schivare il colpo successivo. Il trucco principale qui, era che in diverse prove la possibilità di schivare corrispondeva ancora alla percentuale data al giocatore nel suo foglio delle statistiche.

Questo tipo di sistema mi ha incuriosito moltissimo all'epoca, e ora sono nella situazione di aver bisogno di una soluzione del genere.

Ecco i miei problemi:

  • Immagino che sarei in grado di trovare risorse online sull'implementazione di un tale sistema, ma potrei mancare solo le parole d'ordine rilevanti per trovarlo.
  • Inoltre ho bisogno di questo approccio per adattarsi a un sistema che non è binomiale (cioè due risultati), ma invece contiene 4 eventi reciprocamente esclusivi.

Il mio approccio attuale è simile a quello di un sistema di biglietti della lotteria. Quando si verifica un evento, cambio i pesi a favore di tutti gli altri eventi. Questo potrebbe funzionare se i quattro eventi dovessero essere ugualmente probabili, ma nel mio caso, i bisogni devono essere molto più diffusi. Ma poiché l'evento prevalente si verifica più spesso, sposta i pesi dell'altro molto più del previsto e non riesco a trovare i numeri per gli spostamenti di peso necessari per mantenere il conteggio medio dei biglietti attorno ai valori iniziali che l'evento era dato.

Alcuni indicatori di direzione o un chiaro esempio sarebbero apprezzati.


4
Se vuoi una risposta altamente sofisticata o sofisticata, potresti avere più fortuna a chiedere a Mathematics.SE. I matematici sono a loro agio nel rispondere a domande complicate sulla probabilità. math.stackexchange.com
Kevin - Ripristina Monica il


6
Un'alternativa al sito di matematica in cui avresti maggiori probabilità di capire le risposte è Programmers.SE . Il design dell'algoritmo non è particolarmente in tema di matematica e probabilmente avrai bisogno di elaborare un design iniziale per ottenere input utili.
Lilienthal,

1
Sono d'accordo con Kevin e Lilienthal che potresti ottenere una risposta migliore lì, ma leggendo la risposta di mklingen ho capito che ciò che viene descritto qui può essere modellato come una catena di Markov e che potrebbe essere uno strumento utile per gli sviluppatori di giochi. Proverò a scriverlo più dettagliatamente in seguito.
esito

1
Mentre sto eseguendo i numeri su alcune delle risposte qui, sto scoprendo che ci sono una serie di vincoli diversi e che una soluzione che li risolva tutti potrebbe essere più complessa di ciò di cui hai bisogno. Alcuni dettagli più specifici sul tuo caso d'uso potrebbero aiutare a restringere le opzioni migliori. Ad esempio, le probabilità dei tuoi eventi sono abbastanza simili (ad es. 5 risultati diversi con una probabilità del 20% ciascuno) o molto diverse (ad es. Il 10% manca l'80% e il 10% critico)? Vuoi ridurre al minimo le corse (ad es. 3 mancate di fila) o blocchi / attese (ad esempio 3 mancate su 8 tentativi o 20 tentativi prima di ottenere un critico)?
DMGregory

Risposte:


19

Fondamentalmente, quello che stai chiedendo è un generatore di eventi "semi-casuale" che genera eventi con le seguenti proprietà:

  1. La frequenza media alla quale si verifica ciascun evento è specificata in anticipo.

  2. È meno probabile che si verifichi lo stesso evento due volte di seguito rispetto a quanto accadrebbe a caso.

  3. Gli eventi non sono completamente prevedibili.

Un modo per farlo è innanzitutto implementare un generatore di eventi non casuale che soddisfi gli obiettivi 1 e 2, quindi aggiungere un po 'di casualità per soddisfare l'obiettivo 3.


Per il generatore di eventi non casuale, possiamo usare un semplice algoritmo di dithering . In particolare, p 1 , p 2 , ..., p n siano le probabilità relative degli eventi da 1 a n e s s = p 1 + p 2 + ... + p n sia la somma dei pesi. Possiamo quindi generare una sequenza non casuale di eventi equidistribuita al massimo utilizzando il seguente algoritmo:

  1. Inizialmente, lascia e 1 = e 2 = ... = e n = 0.

  2. Per generare un evento, incremento ciascuna e io da p I , e l'uscita l'evento k per il quale e k è il più grande (rompere i legami in qualsiasi modo si desidera).

  3. Decremento e k da s , e ripetere dal punto 2.

Ad esempio, dati tre eventi A, B e C, con p A = 5, p B = 4 e p C = 1, questo algoritmo genera qualcosa come la seguente sequenza di output:

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

Nota come questa sequenza di 30 eventi contenga esattamente 15 As, 12 Bs e 3 Cs. Non si distribuisce in modo abbastanza ottimale - ci sono alcune occorrenze di due come di seguito, che avrebbero potuto essere evitate - ma si avvicina.


Ora, per aggiungere casualità a questa sequenza, hai diverse opzioni (non necessariamente reciprocamente esclusive):

  • Puoi seguire il consiglio di Philipp e mantenere un "mazzo" di N prossimi eventi, per un numero N di dimensioni adeguate . Ogni volta che devi generare un evento, scegli un evento casuale dal mazzo, e poi lo sostituisci con il prossimo evento generato dall'algoritmo di dithering sopra.

    Applicando questo esempio all'esempio sopra, con N = 3, si produce ad esempio:

    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

    mentre N = 10 produce l'aspetto più casuale:

    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

    Nota come gli eventi comuni A e B finiscono con molte più corse a causa del mescolamento, mentre i rari eventi C sono ancora abbastanza ben distanziati.

  • Puoi iniettare un po 'di casualità direttamente nell'algoritmo di dithering. Ad esempio, invece di incrementare e i da p i nel passaggio 2, si potrebbe incrementarlo da p i × casuale (0, 2), dove casuale ( un , b ) è un uniformemente distribuito numero casuale tra una e b ; questo produrrebbe un output come il seguente:

    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

    oppure potresti incrementare e i di p i + random (- c , c ), che produrrebbe (per 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

    oppure, per 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

    Si noti come lo schema additivo abbia un effetto randomizzante molto più forte per gli eventi rari C rispetto agli eventi comuni A e B, rispetto a quello moltiplicativo; questo potrebbe o non potrebbe essere desiderabile. Naturalmente, potresti anche utilizzare una combinazione di questi schemi o qualsiasi altra regolazione degli incrementi, purché preservi la proprietà che l' incremento medio di e i sia uguale a p i .

  • In alternativa, è possibile perturbare l' output dell'algoritmo di dithering sostituendo a volte l'evento k scelto con uno casuale (scelto in base ai pesi grezzi p i ). Fintanto che usi lo stesso k nel passaggio 3 dell'output nel passaggio 2, il processo di dithering tenderà comunque a uniformare le fluttuazioni casuali.

    Ad esempio, ecco alcuni esempi di output, con una probabilità del 10% di ogni evento scelto casualmente:

    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

    ed ecco un esempio con una probabilità del 50% che ogni output sia casuale:

    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

    Potresti anche considerare di inserire un mix di eventi puramente casuali e sottoposti a dithering in un mazzo / pool di miscelazione, come descritto sopra, o forse randomizzare l'algoritmo di dithering scegliendo k in modo casuale, come pesato dagli e i (trattando i pesi negativi come zero).

Ps. Ecco alcune sequenze di eventi completamente casuali, con le stesse tariffe medie, per il confronto:

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

Tangente: dato che nei commenti si è discusso sulla necessità o meno, per soluzioni basate sul mazzo, di svuotare il mazzo prima che venga riempito, ho deciso di fare un confronto grafico di diverse strategie di riempimento del mazzo:

Tracciare
Trama di diverse strategie per generare lanci di monete semi-casuali (con un rapporto 50:50 tra testa e croce in media). L'asse orizzontale è il numero di lanci, l'asse verticale è la distanza cumulativa dal rapporto previsto, misurata come (teste - code) / 2 = teste - lanci / 2.

Le linee rosse e verdi sulla trama mostrano due algoritmi non basati sul mazzo per il confronto:

  • Linea rossa, dithering deterministico : i risultati con numero pari sono sempre capi, i risultati con numero dispari sono sempre code.
  • Linea verde, lanci casuali indipendenti : ogni risultato viene scelto indipendentemente a caso, con una probabilità del 50% di teste e una probabilità del 50% di code.

Le altre tre linee (blu, viola e ciano) mostrano i risultati di tre strategie basate sul mazzo, ognuna implementata usando un mazzo di 40 carte, che inizialmente è riempito con 20 carte "testa" e 20 carte "croce":

  • Linea blu, riempi quando è vuota : le carte vengono pescate casualmente fino a quando il mazzo è vuoto, quindi il mazzo viene riempito con 20 carte "testa" e 20 carte "croce".
  • Linea viola, riempita a metà vuota : le carte vengono pescate in modo casuale fino a quando sul mazzo rimangono 20 carte; quindi il mazzo viene riempito con 10 carte "testa" e 10 carte "croce".
  • Linea ciano, riempimento continuo : le carte vengono pescate a caso; i sorteggi pari vengono immediatamente sostituiti con una carta "teste" e i sorteggi dispari con una carta "croce".

Naturalmente, la trama sopra è solo una singola realizzazione di un processo casuale, ma è ragionevolmente rappresentativo. In particolare, puoi vedere che tutti i processi basati sul mazzo hanno una propensione limitata e rimangono abbastanza vicini alla linea rossa (deterministica), mentre la linea verde puramente casuale alla fine si allontana.

(In effetti, la deviazione delle linee blu, viola e ciano lontano da zero è strettamente limitata dalle dimensioni del mazzo: la linea blu non può mai spostarsi più di 10 passi da zero, la linea viola può ottenere solo 15 passi da zero e la linea ciano può spostarsi al massimo a 20 passi da zero. Naturalmente, in pratica, qualsiasi linea che effettivamente raggiunge il suo limite è estremamente improbabile, poiché c'è una forte tendenza per loro a tornare più vicino allo zero se vagano troppo lontano off.)

A prima vista, non vi è alcuna evidente differenza tra le diverse strategie basate sul mazzo (sebbene, in media, la linea blu rimanga un po 'più vicina alla linea rossa e la linea ciano rimanga un po' più lontana), ma un'ispezione più ravvicinata della linea blu rivela uno schema deterministico distinto: ogni 40 disegni (contrassegnati dalle linee verticali grigie tratteggiate), la linea blu incontra esattamente la linea rossa a zero. Le linee viola e ciano non sono così strettamente vincolate e possono stare lontane da zero in qualsiasi punto.

Per tutte le strategie basate sul mazzo, l'importante caratteristica che mantiene limitata la loro variazione è il fatto che, mentre le carte vengono pescate casualmente dal mazzo, il mazzo viene riempito in modo deterministico. Se le carte usate per riempire il mazzo fossero esse stesse scelte casualmente, tutte le strategie basate sul mazzo diventerebbero indistinguibili dalla pura scelta casuale (linea verde).


Risposta molto elaborata. L'aggiunta di fattori casuali all'algoritmo di dithering sembra semplice. :)
Sonaten,

Ho deciso di andare con la tua risposta. :) Ma ti consiglio di mettere le aggiunte della panoramica del metodo in alto. Quello che sto per fare, in base alla tua risposta, è provare sia la soluzione "Rossa" che "Viola".
Sonaten,

53

Non tirare i dadi, distribuisci le carte.

Prendi tutti i possibili risultati del tuo RNG, inseriscili in un elenco, mescolali in modo casuale e restituisci i risultati in ordine casuale. Quando sei alla fine dell'elenco, ripeti.

I risultati saranno comunque distribuiti uniformemente, ma i risultati individuali non si ripeteranno a meno che anche l'ultimo dell'elenco non sia il primo di quello successivo.

Quando questo è un po 'troppo prevedibile per i tuoi gusti, potresti usare un elenco che è nil numero di risultati possibili e inserire ogni risultato possibile nprima di mescolarlo. Oppure puoi rimescolare l'elenco prima che venga ripetuto completamente.


1
ricerca "shuffle bag" (anche su questo sito)
jhocking del

3
Questo è il numero di giochi Tetris che evitano di lasciare il giocatore affamato per pezzi chiave per troppo tempo. È importante svuotare il sacco / mazzo come suggerisce Philipp prima di inserire nuove carte se si desidera controllare le occorrenze in un intervallo prestabilito. Reinserendo le carte mentre procedi (o riaggiustando i pesi), puoi distorcere la distribuzione delle probabilità in modi che sono difficili da calcolare e facili da sbagliare.
DMGregory

2
@DMGregory: In realtà, va benissimo mescolare nuove carte prima che il mazzo venga svuotato (e, in effetti, consiglierei di farlo per rendere i risultati più naturali e più difficili da prevedere). L'importante è assicurarsi che la frazione (media) di nuove carte rimescolate nel mazzo sia uguale alla frazione desiderata che si desidera pescare da essa.
Ilmari Karonen,

4
Illmari Karonen: quando si sostituiscono gli oggetti, è possibile perdere i benefici della borsa shuffle in termini di limiti di esiti identici o lunghi spazi tra esiti particolari. Se il tuo tasso di sostituzione è uguale alla distribuzione di probabilità target, ora sei nella stessa posizione della generazione di ogni risultato in modo indipendente a caso. Se non è uguale alla distribuzione di probabilità target, allora puoi deformare le probabilità effettive in modi che sono difficili da prevedere e bilanciare di conseguenza - il richiedente descrive come affrontare esattamente questo problema.
DMGregory

2
Concordato con @DMGregory. Mescolando nuove carte, invalidi il sistema stesso. Il sistema di carte è specificamente e perfettamente adatto per il risultato desiderato. Ad esempio, quando rimuovi una regina (per usare carte tradizionali per esempio) dal mazzo, la probabilità di pescare una regina diminuisce e aumenta la probabilità di pescare una carta diversa da una regina. È un sistema autoregolante, se vuoi.
Volte,

17

Potresti provare un grafico casuale di Markov . Considera ogni evento che può capitare di essere un nodo in un grafico. Da ciascun evento, crea un collegamento tra loro che potrebbe eventualmente seguirlo. Ognuno di questi collegamenti è ponderato da qualcosa chiamato probabilità di transizione . Quindi, esegui una camminata casuale del grafico in base al modello di transizione.

Ad esempio, puoi avere un grafico che rappresenta il risultato di un attacco (colpo critico, schivata, ecc.). Inizializza il nodo iniziale su uno scelto a caso date le statistiche del giocatore (basta "lanciare i dadi"). Quindi, al prossimo attacco, decidi cosa succede dopo il modello di transizione.

Bisogna fare attenzione a decidere come ponderare le transizioni. Per prima cosa, tutte le transizioni che escono da un nodo devono sommare ad una probabilità di 1. Una cosa semplice che potresti fare è fare una transizione da ogni nodo a ogni altro nodo, con pesi equivalenti alla probabilità che accadano quegli eventi a priori , dato che l'evento attuale non può ripetersi.

Ad esempio, se hai tre eventi:

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

È possibile impostare il modello di transizione in modo tale che un colpo critico non si ripeta semplicemente ridistribuendo la sua massa di probabilità agli altri eventi in modo uniforme:

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

EDIT: Come dicono i commenti sotto, questo modello non è abbastanza complicato da ottenere il comportamento desiderato. Invece, potresti dover aggiungere più stati aggiuntivi!


1
Lo schema di ponderazione proposto non preserva le probabilità desiderate di ciascuno stato. Effettuando un test empirico con questi numeri, i fallimenti si verificano circa il 41% delle volte e i Critici circa il 25%, molto lontani dai valori di input. Il passaggio agli altri stati proporzionale alle loro probabilità (ad es. Miss ha una probabilità del 25% di andare a Crit e una probabilità del 75% di colpire) fa leggermente meglio, con un tasso di Miss del 44% e un Crit del 17%, ma è ancora non riflettente delle probabilità desiderate nell'input.
DMGregory

Ho dimenticato la regola di bayes :( Verrà ricalcolato più tardi. Potrebbe non essere possibile mantenere la distribuzione di probabilità precedente perché il modello di transizione così com'è esclude possibili sequenze come CCHM o CHHM o il molto probabile MMHM, ecc.
mklingen

Il vincolo "nessuna ripetizione" potrebbe legare le mani qui, per quanto riguarda i pesi estremamente alti e bassi. Se vuoi che 1 su 10 tentativi sia Critico, l'unico modo in cui questo metodo può soddisfare è alternare 5 Misses e 5 hit, che distorce le probabilità di hit & miss verso la loro media. Nessuna sequenza senza errori consecutivi può soddisfare i requisiti di input qui.
DMGregory

4
@mklingen, sono d'accordo con DMGregory, il "rigorosamente nessuna ripetizione" non è auspicabile qui. Piuttosto, vogliono che la probabilità di lunghe catene dello stesso risultato sia meno probabile di quanto non sarebbe con una probabilità casuale uniforme. Potresti farlo con una catena di Markov (che è diretta) che assomiglia a questo . Questo utilizza più stati per rappresentare eventi ripetuti in cui le probabilità di passaggio da "Hit 1" a "Hit 2" e "Hit 2" a "Hit 3+" diminuiscono e le probabilità di tornare a "Hit 1" e "Crit 1 "salire.
esito

@ncome risultato è un'ottima idea.
mklingen,

3

Ecco un'implementazione che ho creato in C # che:

  • Attiva eventi in base alle probabilità
  • Regola tali probabilità per ridurre le possibilità di eventi ricorrenti
  • Non allontanarsi troppo dalle probabilità originali

Ho aggiunto alcuni commenti in modo che tu possa vedere cosa sto facendo.

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

Spero che questo aiuti, ti preghiamo di suggerire miglioramenti a questo codice nei commenti, grazie!


1
Questo schema di ripensamento tende a rendere gli eventi equiprobabili. La reimpostazione periodica dei pesi è in realtà solo un cerotto che limita il livello di malfunzionamento, garantendo al contempo che 1 su 10 rotoli non tragga alcun beneficio dal peso. Inoltre, una nota dell'algoritmo: stai sprecando molto lavoro compilando una tabella di 100 voci per fare la tua selezione casuale. Invece, puoi generare un tiro casuale e quindi scorrere i 4 risultati, sommando le loro probabilità mentre procedi. Non appena il risultato è inferiore alla somma, hai il tuo risultato. Nessun riempimento della tabella richiesto.
DMGregory

3

Vorrei generalizzare un po ' la risposta di mklingen . Fondamentalmente, vuoi implementare Fallacy's Gambler , anche se fornirò qui un metodo più generale:

Supponiamo che ci siano npossibili eventi con probabilità p_1, p_2, ..., p_n. Quando si iverifica un evento , la sua probabilità deve ridimensionare con un fattore 0≤a_i≤1/p_i(quest'ultimo è importante, altrimenti si finisce con una probabilità maggiore di uno e gli altri eventi devono avere probabilità negative , che in sostanza significano " anti ". O qualcosa), sebbene tipicamente a_i<1. Ad esempio a_i=p_i, puoi scegliere , il che significa che la probabilità che un evento si verifichi una seconda volta è la probabilità originale che l'evento si verifichi esattamente due volte di seguito, ad esempio un secondo lancio di monete avrebbe una probabilità di 1/4 anziché 1/2. D'altra parte, puoi anche averne alcuni a_i>1, il che significherebbe innescare un "colpo di fortuna / sfortuna".

Tutti gli altri eventi devono rimanere ugualmente probabili l'uno rispetto all'altro, vale a dire che devono essere tutti riscalati con lo stesso fattore b_i tale che la somma di tutte le probabilità sia uguale a uno, ovvero

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)

Finora così semplice. Ma ora aggiungiamo un altro requisito: considerando tutte le possibili sequenze di due eventi, le probabilità di singolo evento da esse estratte devono essere le probabilità originali.

Permettere

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

denota la probabilità che si jverifichino eventi dopo evento ie nota che a p_ij≠p_jimeno b_i=b_j (2)(che (1)implica a_j = 1 - a_i*p_i + (1-a_i)*p_i/p_j). Questo è anche ciò che richiede il teorema di Bayes e questo implica anche

Σ_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)

proprio come desiderato. Basta notare come questo significhi unoa_i risolve tutti gli altri.


Ora vediamo cosa succede quando applichiamo questa procedura più volte, ovvero per sequenze di tre e più eventi. Esistono sostanzialmente due opzioni per la scelta delle probabilità truccate del terzo evento:

a) Dimentica il primo evento e il rig come se si fosse verificato solo il secondo, ad es

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

Si noti che questo di solito viola Bayes, poiché ad esempio p_jik≠p_ikjnella maggior parte dei casi.

b) Usa usa le probabilità p_ij(per fisso i) come nuove probabilità pi_jda cui ottieni le nuove probabilità pi_jkche l'evento kaccada in seguito. La modifica ai_jo meno dipende da te, ma tieni presente che i nuovi bi_jsono decisamente diversi a causa dei cambiamenti pi_j. Inoltre, la scelta di ai_jè probabilmente limitata richiedendo che tutte le permutazioni si ijkverifichino con la stessa probabilità. Vediamo...

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

e loro permutazioni cicliche, che devono essere uguali per i rispettivi casi.

Temo che la mia continuazione su questo dovrà aspettare un po '...


Testando empiricamente, ciò si traduce ancora in una distorsione dalle probabilità di input su molte esecuzioni. Se a_i / p_i = 0,5 per esempio (e usando i numeri dalla risposta di mklingen) un tasso di mancato ingresso del 60% diventa un tasso osservato del 50,1% e un tasso critico di ingresso del 10% viene osservato come 13,8%. È possibile verificarlo portando la matrice di transizione risultante ad alta potenza. La scelta dei rapporti di a_i: p_i più vicino a 1 comporta una minore distorsione, ma anche una minore efficacia nel ridurre le corse.
DMGregory

@DMGregoria buon punto: non puoi semplicemente prendere i poteri della matrice di transizione. In seguito espanderò la mia risposta
Tobias Kienzler,

@DMGregory Ho iniziato a descrivere l'intero processo (variante b)), ma diventa abbastanza noioso e al momento sono a corto di tempo: /
Tobias Kienzler

1

Penso che l'opzione migliore sia quella di utilizzare la selezione degli articoli ponderata in modo casuale. C'è un'implementazione per C # qui , ma possono essere facilmente trovati o realizzati anche per altre lingue.

L'idea sarebbe quella di ridurre il peso di un'opzione ogni volta che viene selezionato e aumentarlo ogni volta che non viene selezionato.

Ad esempio, se riduci il peso dell'opzione selezionata di NumOptions-1e aumenti il ​​peso di ogni altra opzione di 1 (facendo attenzione a rimuovere gli articoli con peso <0 e li leggi quando superano lo 0) , ogni opzione verrà scelta approssimativamente lo stesso numero di volte per un lungo periodo, ma le opzioni scelte di recente avranno molte meno probabilità di essere raccolte.


Il problema con l'utilizzo di un ordinamento casuale, come suggerito da molte altre risposte, è che dopo aver scelto ogni opzione tranne una, è possibile prevedere con certezza al 100% quale opzione verrà scelta successivamente. Non è molto casuale.


1

La mia risposta non è corretta, il mio test era difettoso.

Lascio qui questa risposta per la discussione e i commenti che sottolineano i difetti di questo progetto, ma il test effettivo non è corretto.

Quello che stai cercando è un peso ponderato: i pesi per i tuoi quattro possibili risultati devono essere ulteriormente regolati (ponderati) dai risultati precedenti, pur rimanendo nel complesso i pesi corretti.

Il modo più semplice per ottenere questo risultato è alterare tutti i pesi per ciascun rotolo diminuendo il peso per il valore specifico ottenuto e aumentando gli altri pesi .

Ad esempio, supponiamo che tu abbia 4 pesi: Fumble, Miss, Hit e Crit. Diciamo anche che i pesi complessivi desiderati per loro sono Fumble = 10%, Miss = 50%, Hit = 30% e Crit = 10%.

Se si utilizza un generatore di numeri casuali (RNG) per produrre valori compresi tra 1 e 100, quindi confrontare tale valore con il punto in cui rientra in questo intervallo (1-10 Fumble, 11-60 miss, 61-90 hit, 91-100 crit ), stai generando un singolo tiro.

Se, quando effettui quel tiro, modifichi immediatamente tali intervalli in base al valore ottenuto, peserai i rulli futuri, ma dovrai anche ridurre il peso ottenuto dello stesso importo totale di cui aumenti gli altri pesi. Quindi nel nostro esempio sopra, ridurrebbe il peso rotolato di 3 e aumenterebbe gli altri pesi di 1 ciascuno.

Se lo fai per ogni tiro, avrai comunque la possibilità di serie, ma saranno notevolmente ridotte, perché per ogni tiro aumenti la possibilità che i tiri futuri siano qualcosa di diverso da quello attuale. È possibile aumentare questo effetto e quindi ridurre ulteriormente la possibilità di strisce, aumentando / diminuendo i pesi di un fattore maggiore (ad esempio ridurre la corrente di 6 e aumentarne altri di 2).

Ho eseguito un'app rapida per convalidare questo approccio e, dopo 32000 iterazioni con quei pesi, produce i seguenti grafici. Il grafico superiore mostra i 4 valori immediati dei pesi per ogni lancio e il grafico inferiore mostra il conteggio di somma di ciascun tipo di risultato ottenuto fino a quel punto.

Come puoi vedere, i pesi fluttuano leggermente attorno ai loro valori desiderati, ma i pesi complessivi rimangono all'interno degli intervalli desiderati e dopo che la varietà iniziale dei numeri iniziali si assesta, i risultati si adattano quasi perfettamente alle nostre percentuali desiderate.

Si noti che questo esempio è stato prodotto utilizzando la classe .NET System.Random, che in realtà non è uno dei migliori RNG disponibili, quindi è possibile ottenere risultati più accurati utilizzando un RNG migliore. Si noti inoltre che 32000 è stato il massimo risultato che ho potuto rappresentare graficamente con questo strumento, ma il mio strumento di test è stato in grado di generare oltre 500 milioni di risultati con gli stessi schemi generali.


Nota che questo funziona solo se i tuoi +1 + / -3 vengono applicati in relazione ai pesi originali, piuttosto che ai pesi utilizzati più di recente. (Modificando continuamente i pesi in modo uniforme in questo modo li si sposta verso l'equipaggiamento). Mentre questo mantiene la probabilità sul bersaglio nel lungo periodo, fa ben poco per ridurre le corse. Dato che mi sono perso una volta, la possibilità che mi mancherà altre due volte di seguito è del 22% con questo schema, contro il 25% con pareggi indipendenti. Aumentare lo spostamento del peso per un effetto maggiore (diciamo a + 3 / -9) comporta una distorsione della probabilità a lungo termine.
DMGregory

In realtà i dati presentati sopra applicano il + 1 / -3 al peso più recente ogni volta che viene elaborato un rotolo. Quindi se perdi una volta il peso iniziale del 50%, il peso mancante successivo sarebbe del 47% e, se perdi ancora, il peso seguente sarebbe del 44% e così via. Riduce le corse (la metrica separata stava monitorando le corse, riscontrate fino a una riduzione del 24% nelle corse), ma sono ancora inevitabili in quanto questo schema ha ancora una forte probabilità di lasciare ciascuno dei 4 pesi con una probabilità diversa da zero ( ad esempio, quattro critici di fila lascerebbero il peso critico senza possibilità di verificarsi).
David C Ellis,

Se quello era il tuo intento, la tua implementazione ha un bug. Guarda il grafico - Il peso della covata rimbalza sempre tra 7 e 11, senza valori al di fuori di questo. Ho eseguito una simulazione usando la modifica continua che descrivi e i grafici sono drasticamente diversi, con le probabilità di ciascuno stato di convergere verso il 25% ciascuno entro le prime cento prove.
DMGregory

Dannazione, in effetti era infastidito come hai sottolineato. Bene, colpisci questa risposta.
David C Ellis,

@DavidCEllis stai dicendo che l'implementazione era difettosa o l'idea stessa è? La mia intuizione sul retro di un tovagliolo è arrivata approssimativamente al modello che descrivi (aggiusta una probabilità in basso quando disegnato, ripristina gradualmente tutte le probabilità ai loro valori originali nel tempo) e ha ancora senso per me.
dimo414,

0

Potresti fare ciò che è essenzialmente un filtro. Tieni traccia degli n eventi passati. La probabilità è alcuni di alcuni filtri applicati a quegli eventi. Il filtro 0 è la probabilità di base, se 0 allora hai schivato, se 1 hai fallito. Diciamo che la base era del 25% e il filtro diminuisce della metà di ogni iterazione. Il tuo filtro sarebbe quindi:

[.25 .125 .0625 .03125] 

Sentiti libero di continuare se lo desideri. La probabilità complessiva di questo schema è leggermente superiore alla probabilità base di 0,25. In effetti, la probabilità, dato lo stesso schema, è (sto chiamando x la probabilità reale, p è l'input di probabilità):

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

Risolvendo per x, si trova la risposta è p(1+1/2+1/4+1/8)/(1+p(1/2+1/4+1/8), o per il nostro caso determinato, x=0.38461538461. Ma quello che vuoi davvero è trovare p, dato x. Questo risulta essere un problema più difficile. Se hai assunto un filtro infinito, il problema diventax+x*p=2*p , o p=x/(2-x). Quindi aumentando il tuo filtro, potresti quindi risolvere un numero p che ti darà in media gli stessi risultati, ma a un ritmo che dipende da quanto successo è successo di recente.

Fondamentalmente, usi i valori precedenti per determinare quale sia la soglia di accettazione in questo round e prendi un valore casuale. Quindi produce il prossimo valore casuale dato il filtro.


-1

Proprio come ti sei proposto, uno degli approcci a questo è implementare un random ponderato. L'idea è quella di creare un generatore di numeri casuali (o risultati) in cui pesi e risultati possano essere modificati.

Ecco un'implementazione di questo in 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);
    }
}

MODIFICARE Nel caso in cui si desideri regolare automaticamente i pesi, ad esempio aumentare la possibilità di A quando il risultato era B. È possibile

  1. Cambia il comportamento del nextOutcome()metodo, quindi modifica il peso in base al risultato
  2. Utilizzare setWeight()per modificare il peso in base al risultato.

Penso che potresti aver frainteso la domanda: il PO non sta chiedendo come generare risultati casuali ponderati, ma come regolare i pesi per ridurre la probabilità che lo stesso risultato si verifichi più volte di seguito.
Ilmari Karonen,

Vedo, ho modificato alcune delle mie risposte per spiegare come sarebbe possibile utilizzare questo sistema.
erikgaal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.