2012-08-13 42 views
6

Soy nuevo en C#.C# juego de azar

Lo que estoy tratando de hacer

Estoy tratando de crear un juego de sistema oportunidad aquí.

Básicamente esto es lo que es:

Mi pregunta: ¿Cómo hago para lograr lo que estoy tratando de hacer?

+0

Parece que usted está tratando de generar un número al azar y aleatoriamente elegir un elemento de una lista, donde cada elemento de la lista tiene la oportunidad ponderado de ser recogido Probablemente la cosa más simple de hacerlo lo haría sea ​​para resumir las "posibilidades" y aleatorizar entre 0 y la suma (probabilidad), luego elija el elemento que corresponde a ese número. ¿Eso suena bien? –

+0

La clase 'Random' tiene un método' Next (int MaxValue) 'que podría ayudarlo. Haga una pequeña búsqueda en él. –

Respuesta

5

Su código de ejemplo tiene un error: usted ha escrito 150/208 y 190/209. Esta es una división entera, y ambos dan como resultado cero. Debería haber escrito: 150.0/208 y 190.0/209 para indicar al compilador que los divida como números dobles no enteros.

Editar:
Suponiendo generador de números aleatorios del sistema es plana y que la tabla es la siguiente:

[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 

Luego, su aleatoriedad puede verse como:

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"); 

La idea es que uno pone todo los artículos en una línea looong, uno tras otro, pero los mantienes en sus grupos. Entonces, al comienzo hay tres millones del primer tipo, luego un millón y medio del segundo tipo, y así sucesivamente. Hay en total 6820000 artículos en la línea. Ahora selecciona aleatoriamente un número del 1 al 6820000 (o del 0 al 6819999) y úselo como el NÚMERO de un elemento en la LÍNEA.

Como los elementos están presentes en la línea con su distribución estadística correcta, entonces si la aleatorización 1-6820000 fue PLANA, entonces la 'lotería' resultante tendrá distribución exactamente como usted quería.

El único truco que queda por explicar, es cómo adivinar qué elemento se eligió. Es por eso que mantuvimos los artículos en grupos. La primera parte de los 3000000 ítems es el primer tipo, de modo que si el número fue menor a 3000000, entonces seleccionamos el primer tipo. Si es más que eso, pero más bajo que el siguiente 1500000 (menor que 4500000), entonces se golpea el segundo tipo ... y así sucesivamente.

+0

Lo he hecho y ese código funciona ahora. Gracias. ¿Pero crees que es suficiente para lo que estoy tratando de hacer? No soy bueno con las estadísticas tanto. – Jack

+0

Estás en el buen camino. He agregado una explicación larga sobre cómo funciona el 'generador con distribución tabular'. Por favor vuelve a leer mi publicación. – quetzalcoatl

+0

Esto parece funcionar muy bien. He experimentado con esto por un tiempo. Pero me pregunto algunas cosas, lo siento si son demasiadas preguntas. ** 1 ** - ¿Qué quiere decir con asignación al azar plana? ** 2 ** - ¿Importa si eventualmente todos los montos terminan hasta el 100%? ** 3 ** - ¿Por qué no necesitamos usar ningún porcentaje de probabilidad? ¿Importa si no lo hacemos? ** 4 ** - ¿De todos modos puedo probar este código si ya conozco la probabilidad? Por ejemplo, la probabilidad de obtener X es del 80%, así que ejecuto este código en un bucle por 50 veces y luego veo si funciona. Por ejemplo, entonces X debería mostrarse alrededor del 80% de veces en el ciclo, o? – Jack

0

he hecho algo similar en mi aplicación y lo convertirá a su problema a continuación: En pseudocódigo:

  • Suma todos los valores (para obtener el total)
  • Obtener un valor aleatorio entre 0 y la suma
  • Pasa por todos los elementos hasta que ese elemento
  • Al llegar al número aleatorio, ese elemento es el que pertenece al valor.

Los artículos de clase se ve de la siguiente manera (eliminado algunas líneas sin importancia y ha añadido comentarios 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 clase de artículo no es más que un marcador de posición para el nombre y valor.

Parece mucho, pero tiene algunas ventajas:

  • Cuando un cambio de valores, la suma se calcula automáticamente.
  • Al agregar un artículo, solo se necesita cambiar una línea.
1

Como han dicho otros, su código tiene un error de división de enteros.

En cualquier caso, deseará consultar: Muestreo de Transformación Inversa.

Básicamente, le permite tomar un número aleatorio uniforme (lo que la mayoría de los PRNG le dan) y transformarlo en una muestra aleatoria de cualquier distribución. Para hacer esto, necesita usar el CDF de la distribución objetivo.

Referencias & páginas útiles:

[CiteHistory Record]

Editado: I en realidad significó la distribución categórica, no la distribución multinomial. Estas dos distribuciones a menudo se combinan (especialmente en mi campo), pero la diferencia es importante. Las dos distribuciones son equivalentes solo cuando la distribución multinomial se parametriza con n = 1 (es decir, una prueba).

0

Un divisor debe ser un doble para evitar la división de ceros. Para calcular la probabilidad que necesita para acumular ellos hasta el 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; 
} 

como se puede utilizar es:

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

Tenga en cuenta que volver a usar la instancia aleatoria. Si lo creara en el bucle, el "valor aleatorio siempre sería el mismo ya que se sembraría con el mismo tiempo."

+0

Ya probé algo así, el problema fue que 25 nunca se mostró, en cambio 50 se hicieron cargo todo el tiempo. – Jack

+0

@Jack: supongo que la razón de ese comportamiento fue que siempre usaste una nueva instancia aleatoria en un bucle. Aleatorio será sembrado con la hora actual. En un bucle, siempre será el mismo momento, por lo que siempre obtendrá el mismo valor "aleatorio". Esta es la razón por la que paso la instancia aleatoria como parámetro del método. Debe crear la instancia aleatoria fuera del ciclo y siempre reutilizar la misma. Otra opción sería hacer que la variable aleatoria sea miembro en la clase. –

+0

@Jack: Edité mi respuesta para demostrar lo anterior, también cambié las probabilidades porque deben ser acumulativas. –

0

Algo como esto debería hacer. Quizás no sea el mejor ejemplo en el mundo, pero debería ser suficiente:.

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