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?
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?
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.
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
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
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
he hecho algo similar en mi aplicación y lo convertirá a su problema a continuación: En pseudocódigo:
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:
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:
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).
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."
Ya probé algo así, el problema fue que 25 nunca se mostró, en cambio 50 se hicieron cargo todo el tiempo. – Jack
@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. –
@Jack: Edité mi respuesta para demostrar lo anterior, también cambié las probabilidades porque deben ser acumulativas. –
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;
}
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? –
La clase 'Random' tiene un método' Next (int MaxValue) 'que podría ayudarlo. Haga una pequeña búsqueda en él. –