Semplificando alcuni probabilistici If-Then Codice degli spaghetti

4

Ho una base di codice abbastanza sostanziale che sto cercando di semplificare. Una sezione in particolare riguarda la creazione probabilistica di oggetti. Attualmente utilizza centinaia di chiamate al generatore di numeri casuali in istruzioni if-then per ottenere il risultato desiderato.

Esempio:

if rand(20) >= 2 then
    //Make Object A
if rand(20) >= 5 then
    //Make Object A
if rand(20) >= 14 then
    //Make Object A
if rand(20) >= 18 then
    //Make Object A

if rand(100) >= 25 then
    if rand(10) < 8 then
        //Make Object B
    elseif rand(10) < 8 then
        //Make Object C
    else
        //Make Object D

La mia prima idea è di usare una sorta di distribuzione statistica ponderata per scegliere quanti oggetti ho bisogno di fare. Tuttavia, in realtà non so se questo sarebbe un miglioramento nella leggibilità e manutenibilità. Probabilmente non saprei nemmeno come iniziare a farlo con alcuni degli alberi if-then nidificati più grandi.

Gli alberi if-then rientrano in alcune grandi categorie:

  • Crea da 0 a n dello stesso oggetto
  • Crea un singolo oggetto, selezionato tra diverse scelte.
  • I due precedenti combinati (crea da 0 a n oggetti, ciascuno scelto tra diverse possibilità)

Esistono modi più semplici per semplificare il codice in questo modo?

Ho solo bisogno di approssimare il comportamento in una forma facilmente leggibile e modificabile.

    
posta Christopher Sheaf 13.08.2015 - 03:07
fonte

2 risposte

8

Per prima cosa, vorrei conoscere le probabilità che si verifichi un evento. Prendi carta e penna, lo faremo a mano.

Rendi tutto indipendente quando puoi. Ad esempio, quando vedi la serie di if all'inizio:

if rand(20) >= 2 then
    //Make Object A
if rand(20) >= 5 then
    //Make another Object A
if rand(20) >= 14 then
    //Make a third Object A
if rand(20) >= 18 then
    //Make one last Object A

Dovremmo riconoscere che si tratta di 4 estrazioni indipendenti. Sotto ognuno di essi abbiamo due possibilità: creare un oggetto A o "non fare nulla". Assegna a ciascuna una probabilità utilizzando i rapporti, quindi la probabilità della prima istanza dell'istruzione di creare un oggetto è 2/20.

Altre sezioni non ti permetteranno di trattarle come disegni indipendenti a causa dei pattern "elseif".

if rand(100) >= 25 then
    if rand(10) < 8 then
        //Make Object B
    elseif rand(10) < 8 then
        //Make Object C
    else
        //Make Object D

richiederà un albero per descrivere le probabilità

           X
          / \
p=25/100 /   \ p=75/100
   Do nothing X
             / \
   p=8/10   /   \  p=2/10
        Make B   X
                / \
        p=8/10 /   \ p=2/10
           Make C Make D

Ora questo è solo il tentativo di capire cosa è stato scritto. Il prossimo passo è cercare di semplificarlo. L'idea è che puoi mescolare i rotoli in giro, purché le probabilità siano le stesse. Ad esempio, solo con qualche moltiplicazione posso vedere le probabilità di creare un B C o D.

           X
          / \
p=25/100 /   \ p=75/100
   Do nothing X
   (p=.25)   / \
   p=8/10   /   \  p=2/10
        Make B   X
  (p=.75*.8=.6) / \
        p=8/10 /   \ p=2/10
           Make C Make D
(p=.75*.2*.8=.12)  (p=.75*.2*.2=.03)

Ora, osservando tutte queste probabilità, possono essere tutte espresse come un rapporto razionale con 100 in basso (.12 = 12/100; .03 = 3/100). Di conseguenza, possiamo gestire tutto questo con un singolo rotolo, il che rende la logica molto più semplice.

roll = rand(100) -- a random roll from [0,100) is sufficient for the whole block
if roll < 60 then -- we'll hit this with a probability of .6
    // make object B
elseif roll < 72 then -- 72-60 = 12, so we'll hit this with a probability of .12
    // make object C
elseif roll < 75 -- 75-72 = 3, so we'll hit this with a probability of .03
    // make object D
else -- 100-75=25, so we'll hit this with a probability of .25
    -- do nothing.  This is the equivalent of not entering the
    -- outermost if statement, with a probability of .25.
end

L'altro schema che vedo qui è che ci sono molti disegni indipendenti che creano tutti lo stesso oggetto. Hai ragione: sarebbe bello semplificarli. Puoi usare una tabella di probabilità congiunta per unirti a loro. Ad esempio, possiamo prendere i primi due casi "make object A".

                   Nothing(p=.3)              Make A (p=.7)  <-- second if
v--- first 'if'  +-------------------------+--------------------------+
Nothing(p=.15)   | Nothing (p=.3*.15=.045) | Make A (p=.7*.15=.105)   |
                 +-------------------------+--------------------------+
Make A (p=.85)   | Make A (p=.3*.85=.255)  | Make 2x A (p=.7*.85=.595 |
                 +-------------------------+--------------------------+

Puoi applicare questo processo tutte le volte che vuoi, finché non ottieni una tabella contenente la probabilità di ottenere 1A, 2A, 3A o 4A.

Quando hai finito con tutto questo, ciò che dovresti vedere è un piccolo numero di estrazioni, seguito da una ricerca da poche tabelle per determinare il risultato in base a tali estrazioni. Questo dovrebbe essere sufficiente per afferrare saldamente il codice spaghetti di probabilità.

Da lì, è possibile utilizzare alcune programmazioni funzionali per rendere questo più pulito. È possibile creare un oggetto tabella che ricerca un'estrazione e restituisce una funzione che eseguirà l'attività corretta per quello slot nella tabella. Questo ha il vantaggio di essere un molto modo standard per ottenere risultati probabilistici, quindi qualsiasi sviluppatore futuro che lo guardi riconoscerà immediatamente la tabella di estrazione casuale e si sentirà a suo agio con esso.

    
risposta data 13.08.2015 - 06:58
fonte
0

Quando mi sono trovato di fronte a qualcosa del genere una volta indietro la mia soluzione:

Tutte queste informazioni sono state memorizzate come file di testo esterno, caricato all'avvio del programma. Era una raccolta di tabelle (tutta la risoluzione simbolica veniva eseguita al caricamento, nessuna ricerca quando veniva chiamata) con un elenco delle probabilità di quella voce e una lista di voci o un puntatore a un'altra tabella. Avere tutto in un file di testo modificabile dall'uomo ha reso la vita molto più facile. Il caricatore ha anche riassunto tutte le probabilità, se in futuro avessi allungato un tavolo non avresti dovuto cercare di ribilanciare tutto in una tabella per renderlo totale dello stesso totale (come il tuo rand (20)).

    
risposta data 13.08.2015 - 05:45
fonte

Leggi altre domande sui tag