Java, $ 806,899
Esto es de una prueba de 2501 rondas. Todavía estoy trabajando en optimizarlo. Escribí dos clases, una envoltura y un jugador. El contenedor crea una instancia del jugador con el número de sobres (siempre 10000 para el objeto real) y luego llama al método takeQ
con el valor del sobre superior. El jugador luego regresa true
si lo toman, false
si lo pasan.
Jugador
import java.lang.Math;
public class Player {
public int[] V;
public Player(int s) {
V = new int[s];
for (int i = 0; i < V.length; i++) {
V[i] = i + 1;
}
// System.out.println();
}
public boolean takeQ(int x) {
// System.out.println("look " + x);
// http://www.programmingsimplified.com/java/source-code/java-program-for-binary-search
int first = 0;
int last = V.length - 1;
int middle = (first + last) / 2;
int search = x;
while (first <= last) {
if (V[middle] < search)
first = middle + 1;
else if (V[middle] == search)
break;
else
last = middle - 1;
middle = (first + last) / 2;
}
int i = middle;
if (first > last) {
// System.out.println(" PASS");
return false; // value not found, so the envelope must not be in the list
// of acceptable ones
}
int[] newVp = new int[V.length - 1];
for (int j = 0; j < i; j++) {
newVp[j] = V[j];
}
for (int j = i + 1; j < V.length; j++) {
newVp[j - 1] = V[j];
}
double pass = calcVal(newVp);
int[] newVt = new int[V.length - i - 1];
for (int j = i + 1; j < V.length; j++) {
newVt[j - i - 1] = V[j];
}
double take = V[i] + calcVal(newVt);
// System.out.println(" take " + take);
// System.out.println(" pass " + pass);
if (take > pass) {
V = newVt;
// System.out.println(" TAKE");
return true;
} else {
V = newVp;
// System.out.println(" PASS");
return false;
}
}
public double calcVal(int[] list) {
double total = 0;
for (int i : list) {
total += i;
}
double ent = 0;
for (int i : list) {
if (i > 0) {
ent -= i / total * Math.log(i / total);
}
}
// System.out.println(" total " + total);
// System.out.println(" entro " + Math.exp(ent));
// System.out.println(" count " + list.length);
return total * (Math.pow(Math.exp(ent), -0.5) * 4.0 / 3);
}
}
Envoltura
import java.lang.Math;
import java.util.Random;
import java.util.ArrayList;
import java.util.Collections;
public class Controller {
public static void main(String[] args) {
int size = 10000;
int rounds = 2501;
ArrayList<Integer> results = new ArrayList<Integer>();
int[] envelopes = new int[size];
for (int i = 0; i < envelopes.length; i++) {
envelopes[i] = i + 1;
}
for (int round = 0; round < rounds; round++) {
shuffleArray(envelopes);
Player p = new Player(size);
int cutoff = 0;
int winnings = 0;
for (int i = 0; i < envelopes.length; i++) {
boolean take = p.takeQ(envelopes[i]);
if (take && envelopes[i] >= cutoff) {
winnings += envelopes[i];
cutoff = envelopes[i];
}
}
results.add(winnings);
}
Collections.sort(results);
System.out.println(
rounds + " rounds, median is " + results.get(results.size() / 2));
}
// stol... I mean borrowed from
// http://stackoverflow.com/questions/1519736/random-shuffling-of-an-array
static Random rnd = new Random();
static void shuffleArray(int[] ar) {
for (int i = ar.length - 1; i > 0; i--) {
int index = rnd.nextInt(i + 1);
// Simple swap
int a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
}
Pronto habrá una explicación más detallada, después de que termine las optimizaciones.
La idea central es poder estimar la recompensa de jugar un juego a partir de un conjunto dado de sobres. Si el conjunto actual de sobres es {2,4,5,7,8,9}, y el sobre superior es el 5, entonces hay dos posibilidades:
- Toma el 5 y juega con {7,8,9}
- Pase el 5 y juegue un juego de {2,4,7,8,9}
Si calculamos la recompensa esperada de {7,8,9} y la comparamos con la recompensa esperada de {2,4,7,8,9}, podremos saber si vale la pena tomar el 5.
Ahora la pregunta es, dado un conjunto de sobres como {2,4,7,8,9} ¿cuál es el valor esperado? Descubrí que el valor esperado parece ser proporcional a la cantidad total de dinero en el conjunto, pero inversamente proporcional a la raíz cuadrada del número de sobres en los que se divide el dinero. Esto vino de jugar "perfectamente" varios juegos pequeños en los que todos los sobres tienen un valor casi idéntico.
El siguiente problema es cómo determinar el " número efectivo de sobres". En todos los casos, la cantidad de sobres se conoce exactamente al realizar un seguimiento de lo que ha visto y hecho. Algo así como {234,235,236} es definitivamente tres sobres, {231,232,233,234,235} es definitivamente 5, pero {1,2,234,235,236} realmente debería contar como 3 y no 5 sobres porque el 1 y 2 son casi inútiles, y nunca pasarías un 234 así más tarde podría recoger un 1 o 2. Tuve la idea de usar la entropía de Shannon para determinar el número efectivo de sobres.
Dirigí mis cálculos a situaciones en las que los valores de la envolvente se distribuyen uniformemente en algún intervalo, que es lo que sucede durante el juego. Si tomo {2,4,7,8,9} y trato eso como una distribución de probabilidad, su entropía es 1.50242. Luego hago exp()
para obtener 4.49254 como el número efectivo de sobres.
La recompensa estimada de {2,4,7,8,9} es 30 * 4.4925^-0.5 * 4/3 = 18.87
El número exacto es 18.1167
.
Esta no es una estimación exacta, pero estoy realmente orgulloso de cuán bien se ajusta a los datos cuando los sobres se distribuyen uniformemente en un intervalo. No estoy seguro del multiplicador correcto (estoy usando 4/3 por ahora) pero aquí hay una tabla de datos que excluye el multiplicador.
Set of Envelopes Total * (e^entropy)^-0.5 Actual Score
{1,2,3,4,5,6,7,8,9,10} 18.759 25.473
{2,3,4,5,6,7,8,9,10,11} 21.657 29.279
{3,4,5,6,7,8,9,10,11,12} 24.648 33.125
{4,5,6,7,8,9,10,11,12,13} 27.687 37.002
{5,6,7,8,9,10,11,12,13,14} 30.757 40.945
{6,7,8,9,10,11,12,13,14,15} 33.846 44.900
{7,8,9,10,11,12,13,14,15,16} 36.949 48.871
{8,9,10,11,12,13,14,15,16,17} 40.062 52.857
{9,10,11,12,13,14,15,16,17,18} 43.183 56.848
{10,11,12,13,14,15,16,17,18,19} 46.311 60.857
La regresión lineal entre lo esperado y lo real da un valor R ^ 2 de 0.999994 .
Mi próximo paso para mejorar esta respuesta es mejorar la estimación cuando el número de sobres comienza a ser pequeño, que es cuando los sobres no están distribuidos de manera aproximadamente uniforme y cuando el problema comienza a ser granular.
Editar: si esto se considera digno de bitcoins, acabo de recibir una dirección en 1PZ65cXxUEEcGwd7E8i7g6qmvLDGqZ5JWg
. ¡Gracias! (Esto fue aquí cuando el autor del desafío estaba repartiendo premios).