Java, $806,899
This is from a trial of 2501 rounds. I am still working on optimizing it. I wrote two classes, a wrapper and a player. The wrapper instantiates the player with the number of envelopes (always 10000 for the real thing), and then calls the method takeQ
with the top envelope's value. The player then returns true
if they take it, false
if they pass it.
Player
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);
}
}
Wrapper
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;
}
}
}
A more detailed explanation is coming soon, after I finish optimizations.
The core idea is to be able to estimate the reward from playing a game from a given set of envelopes. If the current set of envelopes is {2,4,5,7,8,9}, and the top envelope is the 5, then there are two possibilities:
- Take the 5 and play a game with {7,8,9}
- Pass the 5 and play a game of {2,4,7,8,9}
If we calculate the expected reward of {7,8,9} and compare it to the expected reward of {2,4,7,8,9}, we will be able to tell if taking the 5 is worth it.
Now the question is, given a set of envelopes like {2,4,7,8,9} what is the expected value? I found the the expected value seems to be proportional to the total amount of money in the set, but inversely proportional to the square root of the number of envelopes that the money is divided into. This came from "perfectly" playing several small games in which all of the envelopes have almost identical value.
The next problem is how to determine the "effective number of envelopes." In all cases, the number of envelopes is known exactly by keeping track of what you've seen and done. Something like {234,235,236} is definitely three envelopes, {231,232,233,234,235} is definitely 5, but {1,2,234,235,236} should really count as 3 and not 5 envelopes because the 1 and 2 are nearly worthless, and you would never PASS on a 234 so you could later pick up a 1 or 2. I had the idea of using Shannon entropy to determine the effective number of envelopes.
I targeted my calculations to situations where the envelope's values are uniformly distributed over some interval, which is what happens during the game. If I take {2,4,7,8,9} and treat that as a probability distribution, its entropy is 1.50242. Then I do exp()
to get 4.49254 as the effective number of envelopes.
The estimated reward from {2,4,7,8,9} is 30 * 4.4925^-0.5 * 4/3 = 18.87
The exact number is 18.1167
.
This isn't an exact estimate, but I am actually really proud of how well this fits the data when the envelopes are uniformly distributed over an interval. I'm not sure of the correct multiplier (I am using 4/3 for now) but here is a data table excluding the multiplier.
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
Linear regression between expected and actual gives an R^2 value of 0.999994.
My next step to improve this answer is to improve estimation when the number of envelopes starts to get small, which is when the envelopes are not approximately uniformly distributed and when the problem starts to get granular.
Edit: If this is deemed worthy of bitcoins, I just got an address at 1PZ65cXxUEEcGwd7E8i7g6qmvLDGqZ5JWg
. Thanks! (This was here from when the challenge author was handing out prizes.)