001package ball.riddler538.ant.taskdefs; 002/*- 003 * ########################################################################## 004 * Solutions for the 538 Riddler 005 * %% 006 * Copyright (C) 2015 - 2022 Allen D. Ball 007 * %% 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 * ########################################################################## 020 */ 021import ball.swing.table.MapsTableModel; 022import ball.swing.table.SimpleTableModel; 023import ball.util.ant.taskdefs.AntTask; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Map; 027import java.util.Random; 028import java.util.TreeMap; 029import lombok.NoArgsConstructor; 030import lombok.ToString; 031import org.apache.tools.ant.BuildException; 032 033import static org.apache.commons.lang3.StringUtils.EMPTY; 034 035/** 036 * {@link.uri http://ant.apache.org/ Ant} {@link org.apache.tools.ant.Task} 037 * to solve 038 * {@link.uri 039 * http://fivethirtyeight.com/features/can-you-win-this-hot-new-game-show/ 040 * Can You Win This Hot New Game Show?} 041 * <p> 042 * Two players go on a hot new game show "Higher Number Wins." The two go 043 * into separate booths, and each presses a button, and a random number 044 * between zero and one appears on a screen. (At this point, neither knows 045 * the s number, but they do know the numbers are chosen from a standard 046 * uniform distribution.) They can choose to keep that first number, or to 047 * press the button again to discard the first number and get a second 048 * random number, which they must keep. Then, they come out of their booths 049 * and see the final number for each player on the wall. The lavish grand 050 * prize -- a case full of bullion gold -- is awarded to the player who kept 051 * the higher number. Which number is the optimal cutoff for players to 052 * discard their first number and choose another? Put another way, within 053 * which range should they choose to keep the first number, and within which 054 * range should they reject it and try their luck with a second number? 055 * </p> 056 * Solution uses the Monte Carlo method. 057 * 058 * {@ant.task} 059 * 060 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 061 */ 062@AntTask("solve-riddle-2016-03-04") 063@NoArgsConstructor @ToString 064public class SolveRiddle20160304Task extends AbstractSimulationTask { 065 @Override 066 public void execute() throws BuildException { 067 super.execute(); 068 069 try { 070 ArrayList<Simulation> simulations = new ArrayList<>(getCount()); 071 072 for (int i = 0, n = getCount(); i < n; i += 1) { 073 simulations.add(new Simulation()); 074 } 075 076 ArrayList<BucketMap> maps = new ArrayList<>(); 077 078 maps.add(new BucketMap()); 079 maps.add(new BucketMap()); 080 081 for (Simulation simulation : simulations) { 082 double[] winner = simulation.getWinnerPicks(); 083 double key = maps.get(0).tailMap(winner[0]).firstKey(); 084 085 maps.get(0).put(key, maps.get(0).get(key) + 1); 086 087 double[] loser = simulation.getLoserPicks(); 088 089 if (winner[0] > max(loser)) { 090 maps.get(1).put(key, maps.get(1).get(key) + 1); 091 } 092 } 093 094 log(new SimpleTableModel(new Object[][] { }, 3) 095 .row("count:", simulations.size(), EMPTY)); 096 log(); 097 log(new MapsTableModel(maps, 098 "first pick", "total wins", 099 "on first pick")); 100 } catch (BuildException exception) { 101 throw exception; 102 } catch (Throwable throwable) { 103 throwable.printStackTrace(); 104 throw new BuildException(throwable); 105 } 106 } 107 108 private double max(double... array) { return array[greatest(array)]; } 109 110 private int greatest(double... array) { 111 int greatest = 0; 112 113 for (int i = 1; i < array.length; i += 1) { 114 if (array[i] > array[greatest]) { 115 greatest = i; 116 } 117 } 118 119 return greatest; 120 } 121 122 private static final Random RANDOM = new Random(); 123 124 @ToString 125 private class Simulation { 126 private final double[][] p; 127 128 public Simulation() { 129 p = 130 new double[][] { 131 new double[] { RANDOM.nextDouble(), RANDOM.nextDouble() }, 132 new double[] { RANDOM.nextDouble(), RANDOM.nextDouble() } 133 }; 134 } 135 136 public int getWinner() { return greatest(max(p[0]), max(p[1])); } 137 138 public double[] getWinnerPicks() { return p[getWinner()]; } 139 140 public int getLoser() { return (getWinner() == 0) ? 1 : 0; } 141 142 public double[] getLoserPicks() { return p[getLoser()]; } 143 } 144 145 private class BucketMap extends TreeMap<Double,Integer> { 146 private static final long serialVersionUID = -5756858279184775705L; 147 148 public BucketMap() { 149 super(); 150 151 for (int i = 0; i <= 100; i += 1) { 152 put((double) i / 100, 0); 153 } 154 } 155 } 156}