Esto es lo que solía determinar el ganador de una batalla en mi applet Imitator de Lords of Conquest. En este juego, similar a tu situación, solo hay un valor de ataque y un valor de defensa. La probabilidad de que el atacante gane es mayor cuantos más puntos tenga el atacante, y menos puntos tenga la defensa, con valores iguales que evalúan un 50% de probabilidad de que el ataque tenga éxito.
Algoritmo
Lanza una moneda al azar.
1a. Jefes: la defensa pierde un punto.
1b. Colas: las cabezas pierden un punto.
Si tanto la defensa como el atacante aún tienen puntos, regrese al paso 1.
Quien tenga 0 puntos pierde la batalla.
3a. Atacante hasta 0: el ataque falla.
3b. Defensa hasta 0: el ataque tiene éxito.
Lo escribí en Java, pero debería ser fácilmente traducible a otros idiomas.
Random rnd = new Random();
while (att > 0 && def > 0)
{
if (rnd.nextDouble() < 0.5)
def--;
else
att--;
}
boolean attackSucceeds = att > 0;
Un ejemplo
Por ejemplo, supongamos que att = 2 y def = 2, solo para asegurarse de que la probabilidad sea del 50%.
La batalla se decidirá en un máximo de n = att + def - 1
lanzamientos de monedas, o 3 en este ejemplo (es esencialmente el mejor de 3 aquí). Hay 2 n combinaciones posibles de lanzamientos de monedas. Aquí, "W" significa que el atacante ganó el lanzamiento de la moneda, y "L" significa que el atacante perdió el lanzamiento de la moneda.
L,L,L - Attacker loses
L,L,W - Attacker loses
L,W,L - Attacker loses
L,W,W - Attacker wins
W,L,L - Attacker loses
W,L,W - Attacker wins
W,W,L - Attacker wins
W,W,W - Attacker wins
El atacante gana en 4/8, o 50% de los casos.
Las matemáticas
Las probabilidades matemáticas que surgen de este algoritmo simple son más complicadas que el algoritmo mismo.
El número de combinaciones donde exactamente x Ls viene dado por la función de combinación:
C(n, x) = n! / (x! * (n - x)!)
El atacante gana cuando hay entre 0
y att - 1
Ls. El número de combinaciones ganadoras es igual a la suma de combinaciones de a 0
través att - 1
, una distribución binomial acumulativa:
(att - 1)
w = Σ C(n, x)
x = 0
La probabilidad de que el atacante ganadora es w divide por 2 n , una probabilidad acumulativa binomial:
p = w / 2^n
Aquí está el código en Java para calcular esta probabilidad para valores arbitrarios att
y def
:
/**
* Returns the probability of the attacker winning.
* @param att The attacker's points.
* @param def The defense's points.
* @return The probability of the attacker winning, between 0.0 and 1.0.
*/
public static double probWin(int att, int def)
{
long w = 0;
int n = att + def - 1;
if (n < 0)
return Double.NaN;
for (int i = 0; i < att; i++)
w += combination(n, i);
return (double) w / (1 << n);
}
/**
* Computes C(n, k) = n! / (k! * (n - k)!)
* @param n The number of possibilities.
* @param k The number of choices.
* @return The combination.
*/
public static long combination(int n, int k)
{
long c = 1;
for (long i = n; i > n - k; i--)
c *= i;
for (long i = 2; i <= k; i++)
c /= i;
return c;
}
Código de prueba:
public static void main(String[] args)
{
for (int n = 0; n < 10; n++)
for (int k = 0; k <= n; k++)
System.out.println("C(" + n + ", " + k + ") = " + combination(n, k));
for (int att = 0; att < 5; att++)
for (int def = 0; def < 10; def++)
System.out.println("att: " + att + ", def: " + def + "; prob: " + probWin(att, def));
}
Salida:
att: 0, def: 0; prob: NaN
att: 0, def: 1; prob: 0.0
att: 0, def: 2; prob: 0.0
att: 0, def: 3; prob: 0.0
att: 0, def: 4; prob: 0.0
att: 1, def: 0; prob: 1.0
att: 1, def: 1; prob: 0.5
att: 1, def: 2; prob: 0.25
att: 1, def: 3; prob: 0.125
att: 1, def: 4; prob: 0.0625
att: 1, def: 5; prob: 0.03125
att: 2, def: 0; prob: 1.0
att: 2, def: 1; prob: 0.75
att: 2, def: 2; prob: 0.5
att: 2, def: 3; prob: 0.3125
att: 2, def: 4; prob: 0.1875
att: 2, def: 5; prob: 0.109375
att: 2, def: 6; prob: 0.0625
att: 3, def: 0; prob: 1.0
att: 3, def: 1; prob: 0.875
att: 3, def: 2; prob: 0.6875
att: 3, def: 3; prob: 0.5
att: 3, def: 4; prob: 0.34375
att: 3, def: 5; prob: 0.2265625
att: 3, def: 6; prob: 0.14453125
att: 3, def: 7; prob: 0.08984375
att: 4, def: 0; prob: 1.0
att: 4, def: 1; prob: 0.9375
att: 4, def: 2; prob: 0.8125
att: 4, def: 3; prob: 0.65625
att: 4, def: 4; prob: 0.5
att: 4, def: 5; prob: 0.36328125
att: 4, def: 6; prob: 0.25390625
att: 4, def: 7; prob: 0.171875
att: 4, def: 8; prob: 0.11328125
Observaciones
Las probabilidades son 0.0
si el atacante tiene 0
puntos, 1.0
si el atacante tiene puntos pero la defensa tiene 0
puntos, 0.5
si los puntos son iguales, menos que 0.5
si el atacante tiene menos puntos que la defensa, y mayores que 0.5
si el atacante tiene más puntos que la defensa .
Tomando att = 50
y def = 80
, necesitaba cambiar a BigDecimal
s para evitar el desbordamiento, pero tengo una probabilidad de aproximadamente 0.0040.
Puede acercar la probabilidad a 0,5 cambiando el att
valor para que sea el promedio de los valores att
y def
. Att = 50, Def = 80 se convierte en (65, 80), lo que arroja una probabilidad de 0.1056.