Losing at the Lottery Made Simple

Most of us know that we shouldn't play the lottery. Statistically, the odds of winning a significant amount of money are tiny to infinitesimal. Sadly, we sometimes have difficulty accepting that we wouldn't win if we tried really hard. I mean, there has to be a way to beat the system, doesn't there?

Well no, there doesn't, but let's not get ahead of ourselves, eh? First, let's create a program where we can try various situations...as it happens, I've created one called lotto.c. (You can also download a gunzip'd version of the file at the bottom of this page.)

lotto.c

/* lotto.c
 *   Simulate people playing the lottery, in particular: Powerball.
 *   Miles V. Aronnax, 4 July 2008.
 * 
 *   Just a legal note: Powerball is copyright Multi-State Lottery Association.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

// ================  User Configurable Settings =====================
// If you core dump on run, you should reduce the players and entries.
// At least, that's the most common probably I ran into.
int number_of_players = 500;
int entries_per_player = 5;

// The numbers table is the list of winning numbers.
// Format:
//   One drawing per line.
//   Six numbers per line one space between each number.
//   The last number should be the "Powerball".
// Personally, I just download the historical drawing list:
//   wget http://www.powerball.com/powerball/winnums-text.txt
// Then I manipulate it like this:
// cat winnums-text.txt | gawk '{print $2,$3,$4,$5,$6,$7}' | grep -iv pb > pbnums.txt
char numbers_table[256] = "pbnums.txt";
// ====================== End of Settings ===========================

// Prize amounts...
// The "case 15" line is the "jackpot" and thus is ill defined.
// All other numbers are from the official Powerball site:
//   http://www.powerball.com/powerball/pb_prizes.asp
int get_prize(int whitematch, int powerball) {
  int matchnum = powerball*10 + whitematch;
  int prize = 0;
  switch(matchnum) {
    case  3: prize = 7; break;
    case  4: prize = 100; break;
    case  5: prize = 200000; break;
    case 10: prize = 3; break;
    case 11: prize = 4; break;
    case 12: prize = 7; break;
    case 13: prize = 100; break;
    case 14: prize = 10000; break;
    case 15: prize = 17000000; break; // Jackpot! $17 Million in this case.
  }
  return prize;
}

// Main program...
int main() {
  printf("\nWelcome to the lotto simulator.\n");

  // Create data storage structures...
  struct { int balance; } player[number_of_players];
  struct {
    int player_id;
    int powerball;
    int whiteball[5];
  } entry[number_of_players * entries_per_player];

  // Seed the random number generator...
  srand(time(NULL));

  // Initialize players and entries...
  // Sorry, I didn't feel like making these variables self documenting.
  printf("  Setting up players with lotto numbers...\n");
  int idx, jdx, kdx, pbc, wbc, i, j, dup;
  for(idx=0; idx < number_of_players; idx++) {

    // Players start with no money...
    // This makes it obvious if the player breaks even or better.
    player[idx].balance = 0;

    // Generate player entries...
    for(jdx=0; jdx < entries_per_player; jdx++) {
      kdx = idx * entries_per_player + jdx;
      entry[kdx].player_id = idx;
      entry[kdx].powerball = rand()%42 + 1;
      for(wbc=0; wbc < 5; wbc++) { entry[kdx].whiteball[0] = rand()%55 + 1; }

      // Check that we didn't get the same ball twice.
      // Yes, there are better ways to do this, but I
      // decided to leave them as an excercise for you.
      dup = 1;
      while(dup == 1) {
        dup = 0;
        for(i=0; i < 5; i++) {
          for(j=0; j < 5; j++) {
            if(i != j) {
              if(entry[kdx].whiteball[i] == entry[kdx].whiteball[j]) {
                dup = 1;
                entry[kdx].whiteball[j] = rand()%55 + 1;
              }
            }
          }
        }
      }
    }
  }

  // Let's start playing...
  printf("  Reading numbers table...\n");
  int winnums[6], drawings = 0;
  char line[100];
  FILE *numtab;
  numtab = fopen(numbers_table, "r");
  if( numtab==NULL ) { printf("Err: Couldn't open numbers table.\n"); exit(0); }

  // For each drawing...
  printf("  Playing lotto...\n");
  while(!feof(numtab)) {

    // Get winning numbers...
    fgets(line, 100, numtab);
    if( strlen(line) < 9 ) { continue; } // skip blank line.
    //printf("   Read line=[%s]\n", line);
    winnums[0] = atoi(strtok(line, " "));
    winnums[1] = atoi(strtok(NULL, " "));
    winnums[2] = atoi(strtok(NULL, " "));
    winnums[3] = atoi(strtok(NULL, " "));
    winnums[4] = atoi(strtok(NULL, " "));
    winnums[5] = atoi(strtok(NULL, "\n")); // Last number is Powerball.
    drawings++;  // Keep track of how many drawings were in numtab.

    // Check for winners...
    for(idx=0; idx < (number_of_players * entries_per_player); idx++) {
      wbc = 0; pbc = 0; kdx = entry[idx].player_id;
      if( winnums[5] == entry[idx].powerball ) { pbc = 1; }
      for(jdx=0; jdx < 5; jdx++) {
         for(i=0; i < 5; i++) {
           if( entry[idx].whiteball[jdx] == winnums[i] ) { wbc++; }
         }
      }

      // Subtract $1 as the cost of the entry.
      player[kdx].balance += get_prize(wbc, pbc) - 1;
    }
  }

  // Close the file.
  fclose(numtab);
  printf("\n"); // Add a new line to show section ends.

  // Output table for later analysis.
  //printf("Dumping player balances for user analysis...\n\n");
  //for(i=0; i < number_of_players; i++) {
  //  printf("%i %i\n", i, player[i].balance);
  //}

  // Initialize statistics.
  printf("Gathering statistics information...\n");
  struct { int min; int max; float avg; long int tot; } stats;
  stats.min = player[0].balance;
  stats.max = player[0].balance;
  stats.avg = 0.0;
  stats.tot = 0;

  // Statistics: 1st pass.
  printf("  First pass in progress...\n");
  for(idx=0; idx < number_of_players; idx++) {
    stats.tot += player[idx].balance;
    if( player[idx].balance < stats.min ) { stats.min = player[idx].balance; }
    if( player[idx].balance > stats.max ) { stats.max = player[idx].balance; }
  }

  // Statistics: 2nd pass.
  // The results form a pretty solid Poisson distribution
  // I was going to calculate variance here.
  printf("  Second pass in progress...\n\n");
  stats.avg = (stats.tot - 0.0)/number_of_players;

  // Statistics Report
  printf("Player Bank Balance Report\n");
  printf("  Max: %i\n", stats.max);
  printf("  Min: %i\n", stats.min);
  printf("  Avg: %8.2f\n", stats.avg);
  printf("In conclusion, the %i players lost $%i total in %i games.\n",
          number_of_players, abs(stats.tot), drawings);
  printf("\n"); // Add a vertical space.

  // End program main().
  return 0;
}

You can compile this program with the command "gcc -O2 -lm -o lotto lotto.c". Please note that you must recompile to change the number of players and entries per player. I know, I'm just oh so lazy.

Run the program a few times. If you don't believe the results, just read the source. Find a problem, I dare you. No seriously, I always need a little enforced humility so find an error and put me in my place. ;-)

Anyway, unless you've started cheating, I bet the overall theme is loss. Yes, it is just statistics, but no where does it talk about probability in this code. All that happens is each player is given a ticket and then that ticket is compared to the winning numbers. The process is repeated hundreds of times so you can see the overall effect which is exactly what the statistics are trying to tell you: winning is effectively impossible.

Of course, the results of trials can be pretty cool to graph. My run of 60,000 players with 5 entries per person in 1113 games resulting in max=5188, min=-5213, avg=-5010 (you find the variance). Look, the final account balances form a pretty good Poisson distribution!

Histogram of player bank balance after playing over one thousand games.

AttachmentSize
lotto.c.gz2.35 KB