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?
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?
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.
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
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
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
ho fatto qualcosa di simile nella mia app e convertirà al vostro problema di seguito: In pseudo-codice:
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:
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:
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).
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
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
@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. –
@Jack: Modificato la mia risposta per dimostrare sopra, anche cambiato le probabilità perché devono essere cumulative. –
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;
}
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? –
La classe 'Random' ha un metodo' Next (int MaxValue) 'che potrebbe aiutarti. Fai una piccola ricerca su di esso. –