Volver a los desafíos
Fácil 0 resueltos 0% de aceptación

Bandido multibrazo epsilon-greedy

Equilibra exploración y explotación en un bandido multibrazo con selección de acciones epsilon-greedy.

Lo que aprenderás

  • Equilibrar exploración y explotación
  • Actualizar medias acumuladas paso a paso
  • Ver por qué los agentes puramente voraces se atascan

Problema

Ver original (inglés)

Un bandido multibrazo es una fila de máquinas tragaperras (brazos) con pagos desconocidos. Epsilon-greedy equilibra explorar y explotar: con probabilidad epsilon tira de un brazo al azar y, si no, tira del brazo con el valor estimado más alto.

Para que la ejecución se pueda probar, los números aleatorios se proporcionan. Empieza con una estimación Q = 0 y un contador N = 0 para cada brazo. En cada paso t:

  1. Si random_values[t] < epsilon, explora: arm = random_arms[t]. Si no, explota: arm = el brazo con el Q más alto (el índice menor si hay empate).
  2. Recibe reward = rewards[t][arm].
  3. N[arm] ← N[arm] + 1 y Q[arm] ← Q[arm] + (reward - Q[arm]) / N[arm], la media acumulada.

Implementa epsilon_greedy(rewards, epsilon, random_values, random_arms).

Entrada

rewards: Una fila por paso con la recompensa que pagaría cada brazo en ese paso.

epsilon: La probabilidad de exploración.

random_values: Un número aleatorio en [0, 1) por paso.

random_arms: El brazo del que tirar en cada paso si se explora.

Salida

Devuelve las estimaciones Q finales, una por brazo.

Restricciones

1 ≤ pasos ≤ 10.000, 1 ≤ brazos ≤ 100

0 ≤ epsilon ≤ 1, -100 ≤ cada recompensa ≤ 100

Ejemplos

Ejemplo 1
Entrada:
epsilon = 0.1
rewards = [
    [1.0, 3.0],
    [1.0, 3.0],
    [1.0, 3.0],
    [1.0, 3.0]
]
random_arms = [0, 1, 0, 0]
random_values = [0.5, 0.05, 0.7, 0.9]

Salida esperada:
expected = [1.0, 3.0]
Entrada:
double epsilon = 0.1;
std::vector<std::vector<double>> rewards = {
    {1.0, 3.0},
    {1.0, 3.0},
    {1.0, 3.0},
    {1.0, 3.0}
};
std::vector<long long> random_arms = {0, 1, 0, 0};
std::vector<double> random_values = {0.5, 0.05, 0.7, 0.9};

Salida esperada:
std::vector<double> expected = {1.0, 3.0};
Entrada:
double epsilon = 0.1;
double[][] rewards = new double[][] {
    new double[] {1.0, 3.0},
    new double[] {1.0, 3.0},
    new double[] {1.0, 3.0},
    new double[] {1.0, 3.0}
};
long[] random_arms = new long[] {0, 1, 0, 0};
double[] random_values = new double[] {0.5, 0.05, 0.7, 0.9};

Salida esperada:
double[] expected = new double[] {1.0, 3.0};

Explicación

El paso 0 explota; ambos Q son 0, así que se tira del brazo 0 y Q = [1, 0]. El paso 1 explora (0.05 < 0.1) y prueba el brazo 1: Q = [1, 3]. Los pasos 2 y 3 explotan el brazo 1, que es mejor. Resultado: [1, 3].

Ejemplo 2
Entrada:
epsilon = 0.0
rewards = [
    [2.0, 5.0],
    [4.0, 5.0],
    [0.0, 5.0]
]
random_arms = [1, 1, 1]
random_values = [0.5, 0.5, 0.5]

Salida esperada:
expected = [2.0, 0.0]
Entrada:
double epsilon = 0.0;
std::vector<std::vector<double>> rewards = {
    {2.0, 5.0},
    {4.0, 5.0},
    {0.0, 5.0}
};
std::vector<long long> random_arms = {1, 1, 1};
std::vector<double> random_values = {0.5, 0.5, 0.5};

Salida esperada:
std::vector<double> expected = {2.0, 0.0};
Entrada:
double epsilon = 0.0;
double[][] rewards = new double[][] {
    new double[] {2.0, 5.0},
    new double[] {4.0, 5.0},
    new double[] {0.0, 5.0}
};
long[] random_arms = new long[] {1, 1, 1};
double[] random_values = new double[] {0.5, 0.5, 0.5};

Salida esperada:
double[] expected = new double[] {2.0, 0.0};

Explicación

Con epsilon = 0 el agente nunca explora. Sigue tirando del brazo 0, cuya media va 2 → 3 → 2, y nunca descubre que el brazo 1 paga 5. Resultado: [2, 0].

Cómo abordarlo

Inténtalo primero por tu cuenta y luego abre las pistas una a una.

Pista 1

En cada paso, elige entre el brazo aleatorio forzado o el brazo actualmente mejor estimado.

Pista 2

Actualiza el valor de cada brazo con un promedio acumulado para que las recompensas antiguas pesen menos a medida que se acumulan más tiradas.

Pista 3

Desempata eligiendo el brazo de menor índice, y recuerda que solo cambia el brazo elegido en cada paso.

Complejidad esperada

O(steps * arms) tiempo, O(arms) espacio extra

Referencias

Este reto está adaptado para CodeUtility. Sigue el enunciado y las restricciones indicadas arriba.

Aprende de otros

Soluciones de la comunidad

Aún no hay soluciones publicadas. Resuelve este reto para compartir la primera.