Get the Wizard's free newsletter!

Monthly, 100% spam-free

How I select random numbers

Last update: Dec. 4, 2009

Asked & Answered
Specific Games
Baccarat
Bingo
Blackjack
Caribbean Stud Poker
Craps
Horse Racing
Keno
Let it Ride
Lottery
Pai Gow Poker
Poker
Roulette
Slot Machines
Spanish 21
Sports Betting
Texas Holdem
Three Card Poker
Video Poker
Other Casino Games
Non-Casino Games
TV Game Shows
General Topics
Betting Systems
Boyfriends
Casinos
Dealers
General
Online Gaming
Probability
Table Games in general
Taxes on Winnings
The Wizard himself
Back issues
See old columns
Ask the Wizard!
Submit your question...
Some of your analysis is based on random simulations. How did you select the random numbers?

Until December 2001 I used the stock random number generator that came with Microsoft Visual C++ and Visual J++ to create programs. The rand() function returns a "random" integer from 0 to 215-1 (32767). This function is flawed in that the numbers are drawn in a cycle. After 231 (2,147,483,648) calls the cycle starts over again. I also discovered that it repeats odd and even numbers in an even smaller cycle of 217 (131,072).

From Dec 2001 to Oct 2008 I used what I’ll call the Nathan Reynolds code below. Nathan was very patient with me, helping me get this code to compile on Visual Studio 6.0, which wasn’t easy. It seemed to draw good random numbers, but was quite slow at only about 37,000 random numbers per second.

In 2008 someone wrote to me saying that the Nathan Reynolds code was much too slow, and suggested a Mersennse Twister random number generator by Takuji Nishimura and Makoto Matsumoto. After hours of trying, I learned it doesn't work in Visual Studio 6.0, but does work in Visual Studio 2005. I had to make some changes to the code from the source linked to above to get it to work. The following code will draws one billion random numbers in 77 seconds, or about 13 million per second. The function genrand_int32() returns a random integer from 0 to 232-1.

I hope the authors, Takuji Nishimura and Makoto Matsumoto, will not mind me republishing their code.

   
   
  
   
   #include <iostream>
   #include <fstream>
   #include <stdlib.h>
   #include <string.h>
   #include <math.h>
   #include <time.h>
   #include <stdio.h>
   
   using namespace std;
   
   /* Period parameters */  
   #define N 624
   #define M 397
   #define MATRIX_A 0x9908b0dfUL   /* constant vector a */
   #define UPPER_MASK 0x80000000UL /* most significant w-r bits */
   #define LOWER_MASK 0x7fffffffUL /* least significant r bits */
   
   static unsigned long mt[N]; /* the array for the state vector  */
   static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
   
   unsigned long genrand_int32(void);
   
   
   int main()
   {
   	int i;
   	unsigned int rn;
   	__int64 count,RandNum_array[64];
   	time_t starttime,endtime;
   	count=0;
   	for (i=0; i<64; i++)
   		RandNum_array[i]=0;
   	starttime=time(NULL);
   	count=0;
   	for (i=1; i<=1000000000; i++)
   	{
   		count++;
   		rn=genrand_int32()%64;
   		RandNum_array[rn]++;
   	}
   	endtime=time(NULL);
   	for (i=0; i<64; i++)
   		printf("%i\t%I64i\n",i,RandNum_array[i]);
   	printf("Total = %I64i\n",count);
   	printf("Total seconds = %i \n",endtime-starttime);
   	return 1;
   }
   
   
   
   /* initializes mt[N] with a seed */
   void init_genrand(unsigned long s)
   {
       mt[0]= s & 0xffffffffUL;
       for (mti=1; mti<N; mti++) {
           mt[mti] = 
   	    (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
           /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
           /* In the previous versions, MSBs of the seed affect   */
           /* only MSBs of the array mt[].                        */
           /* 2002/01/09 modified by Makoto Matsumoto             */
           mt[mti] &= 0xffffffffUL;
           /* for >32 bit machines */
       }
   }
   
   /* initialize by an array with array-length */
   /* init_key is the array for initializing keys */
   /* key_length is its length */
   /* slight change for C++, 2004/2/26 */
   void init_by_array(unsigned long init_key[], int key_length)
   {
       int i, j, k;
       init_genrand(19650218UL);
       i=1; j=0;
       k = (N>key_length ? N : key_length);
       for (; k; k--) {
           mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
             + init_key[j] + j; /* non linear */
           mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
           i++; j++;
           if (i>=N) { mt[0] = mt[N-1]; i=1; }
           if (j>=key_length) j=0;
       }
       for (k=N-1; k; k--) {
           mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
             - i; /* non linear */
           mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
           i++;
           if (i>=N) { mt[0] = mt[N-1]; i=1; }
       }
   
       mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 
   }
   
   /* generates a random number on [0,0xffffffff]-interval */
   unsigned long genrand_int32(void)
   {
       unsigned long y;
       static unsigned long mag01[2]={0x0UL, MATRIX_A};
       /* mag01[x] = x * MATRIX_A  for x=0,1 */
   
       if (mti >= N) { /* generate N words at one time */
           int kk;
   
           if (mti == N+1)   /* if init_genrand() has not been called, */
               init_genrand(5489UL); /* a default initial seed is used */
   
           for (kk=0;kk<N-M;kk++) {
               y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
               mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
           }
           for (;kk<N-1;kk++) {
               y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
               mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
           }
           y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
           mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
   
           mti = 0;
       }
     
       y = mt[mti++];
   
       /* Tempering */
       y ^= (y >> 11);
       y ^= (y << 7) & 0x9d2c5680UL;
       y ^= (y << 15) & 0xefc60000UL;
       y ^= (y >> 18);
   
       return y;
   }
   
   
 
The following shows the output of the program.
Mersenne Twister Output
Rand. Num. Total
0 15621093
1 15620983
2 15626556
3 15619171
4 15626429
5 15628479
6 15625780
7 15631398
8 15618899
9 15626767
10 15624366
11 15628279
12 15626226
13 15630257
14 15619824
15 15622348
16 15622637
17 15623442
18 15625246
19 15623708
20 15627188
21 15626121
22 15619671
23 15621574
24 15627084
25 15624871
26 15624530
27 15629057
28 15624215
29 15629687
30 15619281
31 15615202
32 15628418
33 15622147
34 15623907
35 15623871
36 15623234
37 15630318
38 15625097
39 15623852
40 15625822
41 15629989
42 15624093
43 15628500
44 15628676
45 15628758
46 15631296
47 15625116
48 15623184
49 15626071
50 15628640
51 15621980
52 15626245
53 15625187
54 15618337
55 15627235
56 15627031
57 15621202
58 15622453
59 15621395
60 15628414
61 15633473
62 15624746
63 15620944

A chi-squared test on the results has a p value of 0.784793212.

©1998-2010 Wizard Of Odds Consulting, Inc. All rights reserved.   Privacy/Terms   Contact   Advertise   About Us   Links

The Wizard's other sites: Wizard of Vegas, Wizard of Macau, Math Problems The Wizard recommends: The Bear Growls,   Casinomeister, Online Casino Suite