2012-08-13 9 views
6

Sono nuovo di C#.C# gioco d'azzardo

Quello che sto cercando di fare

Sto cercando di creare un gioco di sistema di azzardo qui.

Fondamentalmente questo è come è:

La mia domanda: Come posso fare per realizzare quello che sto cercando di fare?

+0

Suona come si sta cercando di generare un numero casuale e casualmente scegliere un elemento da un elenco, in cui ogni elemento della lista ha la possibilità di essere scelto ponderata Probabilmente la cosa più semplice da fare sarebbe riassumere le "probabilità" e randomizzare tra 0 e somma (possibilità), quindi selezionare l'elemento che ricade su quel numero.Qui suona bene? –

+0

La classe 'Random' ha un metodo' Next (int MaxValue) 'che potrebbe aiutarti. Fai una piccola ricerca su di esso. –

risposta

5

Il codice di esempio ha un bug grave: hai scritto 150/208 e 190/209. Questa è una divisione intera ed entrambi risultano in zero. Dovresti aver scritto: 150.0/208 e 190.0/209 per chiedere al compilatore di dividerli come non interi.

Edit:
Supponendo RNG del sistema è piatto e che la tabella è la seguente:

[item] [amount] 
0  3 000 000 
25  1 500 000 
50  2 000 000 
75  300 000 
100  10 000 
150  10 000 (no typo) 
    sum = 6820000 

Allora la vostra randomizer può apparire come:

int randomItemNumber = Random.Next(6820000); // 0..6819999 
if(randomItemNumber < 3000000) 
    Console.WriteLine("Aah, you've won the Item type #0\n"); 
else if(randomItemNumber < 3000000+1500000) 
    Console.WriteLine("Aah, you've won the Item type #1\n"); 
else if(randomItemNumber < 3000000+1500000+2000000) 
    Console.WriteLine("Aah, you've won the Item type #2\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000) 
    Console.WriteLine("Aah, you've won the Item type #3\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000) 
    Console.WriteLine("Aah, you've won the Item type #4\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000+10000) 
    Console.WriteLine("Aah, you've won the Item type #5\n"); 
else 
    Console.WriteLine("Oops, somehow you won nothing, the code is broken!\n"); 

L'idea è che si mettono tutti gli oggetti in una linea sottile, uno dopo l'altro, ma li tenga nei loro gruppi. Quindi, all'inizio ci sono tre milioni del primo tipo, poi un milione e mezzo del secondo tipo e così via. Ci sono in totale 6820000 articoli nella linea. Ora scegli a caso un numero da 1 a 6820000 (o da 0 a 6819999) e usalo come NUMERO di un elemento nella LINE.

Poiché gli articoli sono presenti nella riga con la loro corretta distribuzione statistica, quindi se la randomizzazione 1-6820000 era PIATTA, allora la "lotteria" risultante avrà distribuzione esattamente come desiderato.

L'unico trucco rimasto da spiegare è come indovinare quale elemento è stato selezionato. Questo è il motivo per cui abbiamo mantenuto gli articoli in gruppi. La prima parte di 3000000 elementi è il primo tipo, quindi se il numero è inferiore a 3000000, viene visualizzato il primo tipo. Se più di questo, ma inferiore al successivo 1500000 (inferiore a 4500000), il secondo tipo viene colpito ... e così via.

+0

L'ho fatto e quel codice funziona ora. Grazie. Ma pensi che sia abbastanza per quello che sto cercando di fare? Non sono bravo con le statistiche così tanto. – Jack

+0

Sei in un buon modo. Ho aggiunto una lunga spiegazione su come funziona il 'generatore con distribuzione tabellare'. Per favore rileggi il mio post. – quetzalcoatl

+0

Sembra funzionare molto bene. L'ho sperimentato per un po '. Ma mi chiedo alcune cose, scusa se sono troppe domande. ** 1 ** - Cosa intendi per casualità piatta? ** 2 ** - È importante se alla fine tutti gli importi arrivano al 100%? ** 3 ** - Perché non è necessario utilizzare alcuna possibilità percentuale? Importa se non lo facciamo? ** 4 ** - Esiste comunque la possibilità di testare questo codice se conosco già la probabilità? Per esempio la possibilità di ottenere X è dell'80%, quindi eseguo questo codice in loop per 50 volte e poi vedo se funziona. Ad esempio, X dovrebbe essere visualizzato circa l'80% del ciclo o? – Jack

0

ho fatto qualcosa di simile nella mia app e convertirà al vostro problema di seguito: In pseudo-codice:

  • Sommare tutti i valori (per ottenere il totale)
  • Ottenere un valore casuale compreso tra 0 e la somma
  • Passa attraverso tutti gli elementi sommando tutti i valori fino a quella voce
  • Quando viene raggiunto il numero casuale, quella voce è quella che appartiene al valore.

Le voci di classe appare come segue (rimosso alcune linee poco importanti e ha aggiunto commenti con //

public class Items : List<Item> 
{ 
    public Items() 
    { 
     Add(new Item(0, 3000000)); 
     Add(new Item(25, 1500000)); 
     Add(new Item(50, 2000000)); 
     // etc 
    } 

    /// <summary> 
    /// Returns a random item based on value. 
    /// </summary> 
    /// <returns></returns> 
    public Item GetRandomItem() 
    { 
     var sum = this.Sum(item => item.Value); 
     var randomValue = new Random().Next(sum); 

     // Iterate through itemsuntil found. 
     var found = false; 
     var itemIndex = 0; 
     var visitedValue = 0; 
     while (!found) 
     { 
      var item = this[itemIndex]; 
      if ((visitedValue + item.Value) > randomValue) 
      { 
       found = true; 
      } 
      else 
      { 
       itemIndex++; 
       visitedValue += item.value;     
      } 
     } 

     return this[itemIndex];   
    } 

La classe dell'oggetto non è altro che un segnaposto per il nome e il valore.

Sembra lungo, ma ha alcuni vantaggi:

  • Quando un cambio di valore, la somma viene calcolata automaticamente.
  • Quando si aggiunge un elemento, è necessario modificare solo una riga.
1

Come altri hanno già detto, il codice ha un bug di divisione intero.

In ogni caso, si vorrà osservare: Inverse Transform Sampling.

Fondamentalmente, consente di prendere un numero casuale uniforme (quello che la maggior parte dei PRNG offre) e trasformarlo in un campione casuale da qualsiasi distribuzione. Per fare ciò, è necessario utilizzare il CDF della distribuzione di destinazione.

Riferimenti & pagine utili:

[CiteHistory Record]

A cura: I in realtà significava la distribuzione categoriale, non la distribuzione multinomiale. Queste due distribuzioni sono spesso confuse (specialmente nel mio campo), ma la differenza è importante. Le due distribuzioni sono equivalenti solo quando la distribuzione multinomiale è parametrizzata con n = 1 (cioè una prova).

0

Un divisore deve essere un doppio per impedire gli zeri dalla divisione. Per calcolare la probabilità è necessario cumulare loro fino al 100% (o 1):

//  Element  - Probability  - Cumulative Probability 
//  Item100  10000/6820000  0.001466275659824 
//  Item75  300000/6820000  0.0439882697947214 + 0.001466275659824 
//  Item50  2000000/6820000  0.2932551319648094 + 0.0454545454545454 
//  Item25  1500000/6820000  0.219941348973607 + 0.3387096774193548 
const double Item100 = 0.001466275659824; 
const double Item75 = 0.0454545454545454; 
const double Item50 = 0.3387096774193548; 
const double Item25 = 0.5586510263929618; 

int getRandomItem(Random rnd) 
{ 
    double value = rnd.NextDouble(); 
    if (value <= Item100) 
    { 
     // use one of both possible items (100 or 150) 
     int which = rnd.Next(0, 2); 
     return which == 0 ? 100 : 150; 
    } 
    else if (value <= Item75) 
     return 75; 
    else if (value <= Item50) 
     return 50; 
    else if (value <= Item25) 
     return 25; 
    else 
     return 0; 
} 

Come si usa:

var rnd = new Random(); 
var items = new List<int>(); 
for (int i = 0; i < 100; i++) 
    items.Add(getRandomItem(rnd)); 
Console.Write(string.Join(Environment.NewLine, items)); 

notare che ho riutilizzare l'istanza casuale. Se lo creerei nel ciclo, il "valore casuale sarebbe sempre lo stesso dal momento che sarebbe seminato con lo stesso tempo

+0

Ho già provato qualcosa di simile, il problema era che 25 non venivano mai visualizzati, invece 50 ne prendevano il controllo tutto il tempo. – Jack

+0

@Jack: Presumo che il motivo di questo comportamento sia che hai sempre usato una nuova istanza casuale in un ciclo. Casuale verrà seminato con l'ora corrente. In un ciclo sarà sempre la stessa volta, quindi otterrai sempre lo stesso valore "casuale". Questo è il motivo per cui passo l'istanza casuale come parametro al metodo. Dovresti creare l'istanza casuale al di fuori del ciclo e riutilizzare sempre lo stesso. Un'altra opzione sarebbe quella di rendere casuale una variabile membro nella classe. –

+0

@Jack: Modificato la mia risposta per dimostrare sopra, anche cambiato le probabilità perché devono essere cumulative. –

0

Qualcosa come questo dovrebbe farlo, forse non è l'esempio migliore del mondo, ma dovrebbe bastare:.

class Item 
{ 
    public string Name { get ; private set ; } 
    public int Amount { get ; private set ; } 

    public Item(string name , int amount) 
    { 
     if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("name") ; 
     if (amount < 0) throw new ArgumentException("amount") ; 

     this.Name = name ; 
     this.Amount = amount ; 

     return ; 
    } 
} 
static void Main(string[] args) 
{ 
    Random rng = new Random() ; 
    Item[] items = { new Item("item--0" , 3000000) , 
        new Item("item-25" , 1500000) , 
        new Item("item-50" , 2000000) , 
        new Item("item-75" , 300000) , 
        new Item("item-100" , 10000) , 
        new Item("item-150" , 10000) , 
        } ; 
    int total = items.Sum(x => x.Amount) ; 

    for (int i = 0 ; i < 100 ; ++i) 
    { 
     int r = rng.Next(0, total) ; // get a random integer x such that 0 <= x < total 
     int n = 0 ; 
     Item selected = null ; 
     int lo = 0 ; 
     int hi = 0 ; 
     for (int j = 0 ; j < items.Length ; ++j) 
     { 
      lo = n ; 
      hi = n + items[j].Amount ; 
      n = hi ; 

      if (r < n) 
      { 
       selected = items[j] ; 
       break ; 
      } 

     } 
     Console.WriteLine("iteration {0}. r is {1} <= {2} < {3}. Selected item is {4}" , 
      i , 
      lo , 
      r , 
      hi , 
      selected.Name 
      ) ; 


    } 

    return; 
}