Advertisement
robertmarkbram

Flip a coin endlessly and report on x same results in a row

Sep 8th, 2014
567
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.57 KB | None | 0 0
  1. import java.io.IOException;
  2. import java.nio.file.Files;
  3. import java.nio.file.Path;
  4. import java.nio.file.Paths;
  5. import java.nio.file.StandardOpenOption;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Date;
  8. import java.util.Map;
  9. import java.util.Random;
  10. import java.util.TreeMap;
  11.  
  12. /**
  13.  * <p>
  14.  * Endlessly flip a coin and report on how long it took to get the same result
  15.  * 10, 20, 30, 40 etc times in a row.
  16.  * </p>
  17.  *
  18.  * <p>
  19.  * Compiled and run in JDK 7.
  20.  * </p>
  21.  *
  22.  * <p>
  23.  * Discuss this code:
  24.  * http://robertmarkbramprogrammer.blogspot.com.au/2014/09/flip
  25.  * -and-coin-and-get-same-results-100.html or http://bit.ly/1p1zRtM.
  26.  * </p>
  27.  *
  28.  * <p>
  29.  * See this code in pastebin: http://pastebin.com/7PAGyQcU.
  30.  * </p>
  31.  *
  32.  * <p>
  33.  * See results so far from one continuous run: http://pastebin.com/MG50TYED.
  34.  * </p>
  35.  *
  36.  * <p>
  37.  * Updates.
  38.  * </p>
  39.  * <ol>
  40.  * <li>Tuesday 09 September 2014, 12:33:53 PM. Replaced ints with longs for
  41.  * numbers that are getting long. Allow path to be set for where to write log.
  42.  * Changed time format.</li>
  43.  * <li>Wednesday 10 September 2014, 04:31:31 PM. Added a bit more time
  44.  * information when reporting new counts.</li>
  45.  * </ol>
  46.  *
  47.  * @author Robert Mark Bram
  48.  */
  49. public final class RandomInARow implements Runnable {
  50.  
  51.     /** File we write the log to. */
  52.     private Path file;
  53.  
  54.     /** Where to put logs. */
  55.     private String pathToLogs = "";
  56.  
  57.     /** Time we started this run. */
  58.     private long start;
  59.  
  60.     /** How many milliseconds in a second. */
  61.     final static int TIME_MILLISECONDS_IN_A_SECOND = 1000;
  62.     /** How many milliseconds in a minute. */
  63.     final static int TIME_SECONDS_IN_A_MINUTE = TIME_MILLISECONDS_IN_A_SECOND * 60;
  64.     /** How many milliseconds in an hour. */
  65.     final static int TIME_MINUTES_IN_AN_HOUR = TIME_SECONDS_IN_A_MINUTE * 60;
  66.     /** How many milliseconds in a day. */
  67.     final static int TIME_HOURS_IN_A_DAY = TIME_MINUTES_IN_AN_HOUR * 24;
  68.  
  69.     /** Timestamp format for the log when I wish to report on events. */
  70.     public static final ThreadLocal<SimpleDateFormat> FORMAT_TIMESTAMP = new ThreadLocal<SimpleDateFormat>() {
  71.         @Override
  72.         protected synchronized SimpleDateFormat initialValue() {
  73.             return new SimpleDateFormat("dd MMM yyyy, hh:mm:ss.SSS a");
  74.         }
  75.     };
  76.  
  77.     /** Timestamp format for the log file name. */
  78.     public static final ThreadLocal<SimpleDateFormat> FORMAT_FILE = new ThreadLocal<SimpleDateFormat>() {
  79.         @Override
  80.         protected synchronized SimpleDateFormat initialValue() {
  81.             return new SimpleDateFormat("yyyyMMddHHmmssSSS");
  82.         }
  83.     };
  84.  
  85.     /**
  86.      * Run the simulation.
  87.      *
  88.      * @param args
  89.      *            0 - path to put logs file, can be null/empty.
  90.      */
  91.     public static void main(String[] args) {
  92.         String path = null;
  93.         if (args.length > 0) {
  94.             path = args[0];
  95.         }
  96.         new RandomInARow(path).run();
  97.     }
  98.  
  99.     /**
  100.      * @param thePathToLogs
  101.      *            - where to store logs. May be null or empty.
  102.      */
  103.     public RandomInARow(final String thePathToLogs) {
  104.         if (thePathToLogs != null && thePathToLogs.length() > 0) {
  105.             pathToLogs = thePathToLogs;
  106.         }
  107.     }
  108.  
  109.     /* Endlessly run the simulation, starting at 10 tosses in a row and
  110.      * incrementing by 10 each time. */
  111.     public void run() {
  112.         int target = 10;
  113.         while (true) {
  114.             repeatUntilWeGetCount(target);
  115.             target += 10;
  116.         }
  117.     }
  118.  
  119.     /**
  120.      * @param target
  121.      *            keep going until we get this number of repetitions of the same
  122.      *            flip result
  123.      */
  124.     public void repeatUntilWeGetCount(final int target) {
  125.         start = reportPhase(true);
  126.  
  127.         final TreeMap<Integer, Long> counts = new TreeMap<Integer, Long>();
  128.         final Random random = new Random();
  129.         while (true) {
  130.             rememberCountOfContiguousOccurences(random, counts);
  131.             if (counts.lastKey() >= target) {
  132.                 break;
  133.             }
  134.         }
  135.         long finish = reportPhase(false);
  136.         message("-----\nIt took " + reportTime(finish - start) + " to get ["
  137.                 + target + "] results in a row.\n");
  138.         outputResult(counts);
  139.     }
  140.  
  141.     /**
  142.      * Return a string such as
  143.      * "hours, [0], minutes [0], seconds [0], milliseconds [59]" based on a
  144.      * millisecond timestamp.
  145.      *
  146.      * @param millisecondsTotal
  147.      *            milliseconds taken to perform task
  148.      * @return stringing saying how many hours, minutes, seconds and
  149.      *         milliseconds were taken by <code>millisecondsTotal</code>
  150.      */
  151.     private String reportTime(final long millisecondsTotal) {
  152.         long left = 0;
  153.  
  154.         // Milliseconds.
  155.         long milliseconds = millisecondsTotal % TIME_MILLISECONDS_IN_A_SECOND;
  156.         left = millisecondsTotal - milliseconds;
  157.  
  158.         // Seconds.
  159.         long seconds = (left % TIME_SECONDS_IN_A_MINUTE)
  160.                 / TIME_MILLISECONDS_IN_A_SECOND;
  161.         left = left - (TIME_MILLISECONDS_IN_A_SECOND * seconds);
  162.  
  163.         // Minutes.
  164.         long minutes = (left % TIME_MINUTES_IN_AN_HOUR)
  165.                 / TIME_SECONDS_IN_A_MINUTE;
  166.         left = left - (TIME_SECONDS_IN_A_MINUTE * minutes);
  167.  
  168.         // Hours.
  169.         long hours = (left % TIME_HOURS_IN_A_DAY) / TIME_MINUTES_IN_AN_HOUR;
  170.         left = left - (TIME_MINUTES_IN_AN_HOUR * hours);
  171.  
  172.         // Days
  173.         long days = left / TIME_HOURS_IN_A_DAY;
  174.         left = left - (TIME_HOURS_IN_A_DAY * days);
  175.  
  176.         // Formulate time string.
  177.         StringBuilder sb = new StringBuilder();
  178.         if (days > 0) {
  179.             sb.append(days).append(" days, ");
  180.         }
  181.         if (hours > 0 || days > 0) {
  182.             sb.append(hours).append(" hours, ");
  183.         }
  184.         if (minutes > 0 || hours > 0 || days > 0) {
  185.             sb.append(minutes).append(" minutes, ");
  186.         }
  187.         if (seconds > 0 || minutes > 0 || hours > 0 || days > 0) {
  188.             sb.append(seconds).append(" seconds and ");
  189.         }
  190.         sb.append(milliseconds).append(" milliseconds");
  191.         return sb.toString();
  192.     }
  193.  
  194.     /**
  195.      * Report start or finish.
  196.      *
  197.      * @param start
  198.      *            true if this is reporting the start, false to report the end
  199.      * @return new Date().getTime() - millsecond time stamp.
  200.      */
  201.     private long reportPhase(final boolean start) {
  202.         Date date = new Date();
  203.         long millis = date.getTime();
  204.         String prefix = start ? "\nStarted" : "Finished";
  205.         String suffix = start ? "\n" : "\n";
  206.         message(prefix + " at [" + FORMAT_TIMESTAMP.get().format(date) + "]"
  207.                 + suffix);
  208.         return millis;
  209.     }
  210.  
  211.     /**
  212.      * Output results.
  213.      *
  214.      * @param counts
  215.      *            map of (count, occurrences of count). Count = number of times
  216.      *            we got same result in a row. Occurrences of count = number of
  217.      *            times we kept flipping and got that count.
  218.      */
  219.     private void outputResult(final Map<Integer, Long> counts) {
  220.         for (Integer count : counts.keySet()) {
  221.             message(String.format("How often we flipped the same result [%4d] "
  222.                     + "times in a row: %d.\n", count, counts.get(count)));
  223.         }
  224.     }
  225.  
  226.     /**
  227.      * Flip a coin and count how many times we got the same result. Increment
  228.      * the count for that number.
  229.      *
  230.      * @param random
  231.      *            random number generator
  232.      * @param counts
  233.      *            map of (count, occurrences of count). Count = number of times
  234.      *            we got same result in a row. Occurrences of count = number of
  235.      *            times we kept flipping and got that count.
  236.      */
  237.     private void rememberCountOfContiguousOccurences(final Random random,
  238.             final Map<Integer, Long> counts) {
  239.         Integer count = countContiguousOccurences(random);
  240.         if (counts.containsKey(count)) {
  241.             Long countOccurences = counts.get(count);
  242.             countOccurences++;
  243.             counts.put(count, countOccurences);
  244.         } else {
  245.             counts.put(count, 1l);
  246.             Date currentTime = new Date();
  247.             message(String.format("New count [%3d] at [" //
  248.                     + FORMAT_TIMESTAMP.get().format(currentTime)
  249.                     + "] after %s.\n", count, reportTime(currentTime.getTime()
  250.                     - start)));
  251.         }
  252.  
  253.     }
  254.  
  255.     /**
  256.      * Flip a coin repeatedly until we get a different result to the first toss
  257.      *
  258.      * @param random
  259.      *            random number generator
  260.      * @return number of times we tossed and got the same result
  261.      */
  262.     private int countContiguousOccurences(final Random random) {
  263.         int count = 1;
  264.         final int first = random.nextInt(2);
  265.         while (random.nextInt(2) == first) {
  266.             count++;
  267.         }
  268.         return count;
  269.     }
  270.  
  271.     /**
  272.      * Output message to file and standard out.
  273.      *
  274.      * @param message
  275.      *            message to output
  276.      * @throws IOException
  277.      *             if there is a problem writing to log
  278.      */
  279.     private void message(final String message) {
  280.         if (file == null) {
  281.             file = Paths.get(pathToLogs,
  282.                     "results_" + FORMAT_FILE.get().format(new Date()) + ".txt");
  283.         }
  284.         System.out.print(message);
  285.         try {
  286.             Files.write(file, message.getBytes(), StandardOpenOption.CREATE,
  287.                     StandardOpenOption.APPEND);
  288.         } catch (IOException ioe) {
  289.             System.err.println("Failed to write message [" + message + "].");
  290.             throw new RuntimeException(ioe);
  291.         }
  292.     }
  293.  
  294. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement