Random Numbers

Random numbers are an important topic in computer science, especially with respect to game programming. Without them, games would always make the same move every time you played them. Needless to say, it would be no fun if they always reacted in the exact same way each time you played them. Imagine a simple game like poker where the computer always dealt the same hand every game. After playing the first game, you would have figured out what cards the computer had and you would never lose (or have any fun!)

Generating Random Numbers in C

  • Computers can't generate truly random numbers; they generate pseudo-random numbers.
  • Random numbers are used extensively to test programs; easier than typing input from the keyboard all day.
  • Used in many simulations (e.g. games) to add a sense of "chance" to the program.
  • Computers have built-in algorithms that produce pseudo-random numbers for programs to use.
  • Algorithms that "shuffle" (mix-up) data are also worth studying.
  • One method of accessing these numbers is to call the C library function rand() which will return a random integer between 0 and 32,767 (or larger, depending on the compiler).
  • To use the rand function, you need to include stdlib.h. Example:
    #include <stdio.h>  /* printf */
    #include <stdlib.h> /* rand   */
    
    int main(void)
    {
      int i;
      
      for (i = 0; i < 10; i++)
        printf("%i\n", rand());
        
      return 0;
    }
    Output:
    Microsoft
    (Windows)
    Borland
    (Windows)
    gcc/clang
    (Linux)
    gcc/clang
    (macOS)
    41
    18467
    6334
    26500
    19169
    15724
    11478
    29358
    26962
    24464
    
    130
    10982
    1090
    11656
    7117
    17595
    6415
    22948
    31126
    9004
    
    1804289383
    846930886
    1681692777
    1714636915
    1957747793
    424238335
    719885386
    1649760492
    596516649
    1189641421
    
    16807
    282475249
    1622650073
    984943658
    1144108930
    470211272
    101027544
    1457850878
    1458777923
    2007237709
    

    Constraining and Seeding the Pseudo-Random Number Generator

    Usually, we want to generate random numbers within a specific range, rather than between 0 and 32,767 (or 2,147,483,647). The modulus operator, %, is very handy for this. This example generates 10 numbers between 1 and 10:

    #include <stdio.h>  /* printf */
    #include <stdlib.h> /* rand   */
    
    int main(void)
    {
      int i;
      
      for (i = 0; i < 10; i++)
        printf("%i\n", rand() % 10 + 1);
        
      return 0;
    }
    Output:
    MicrosoftBorlandGNU gcc
    2
    8
    5
    1
    10
    5
    9
    9
    3
    5
    
    1
    3
    1
    7
    8
    6
    6
    9
    7
    5
    
    4
    7
    8
    6
    4
    6
    7
    3
    10
    2
    

    Notice that some numbers are duplicated. This is normal and is what random means.

    If I run the program 5 times, this is the output of the 5 runs: (each column is a run). See a problem?

     2    2    2    2    2
     8    8    8    8    8
     5    5    5    5    5
     1    1    1    1    1
    10   10   10   10   10
     5    5    5    5    5
     9    9    9    9    9
     9    9    9    9    9
     3    3    3    3    3
     5    5    5    5    5
    
    Issues with the pseudo-random number generator:
    #include <stdio.h>  /* printf      */
    #include <stdlib.h> /* rand, srand */
    
    int main(void)
    {
      int i; 
      
      srand(1);
      printf("srand(1): ");
      for (i = 0; i < 5; i++)
        printf("%i  ", rand());
      printf("\n");
    
      srand(2);
      printf("srand(2): ");
      for (i = 0; i < 5; i++)
        printf("%i  ", rand());
      printf("\n");
    
      srand(3);
      printf("srand(3)): ");
      for (i = 0; i < 5; i++)
        printf("%i  ", rand());
      printf("\n");
    
      srand(3);
      printf("srand(3): ");
      for (i = 0; i < 5; i++)
        printf("%i  ", rand());
      printf("\n");
    
      srand(2);
      printf("srand(2): ");
      for (i = 0; i < 5; i++)
        printf("%i  ", rand());
      printf("\n");
    
      return 0;
    }
    gcc on Linux:
    srand(1): 1804289383  846930886  1681692777  1714636915  1957747793
    srand(2): 1505335290  1738766719  190686788  260874575  747983061
    srand(3): 1205554746  483147985  844158168  953350440  612121425
    srand(3): 1205554746  483147985  844158168  953350440  612121425
    srand(2): 1505335290  1738766719  190686788  260874575  747983061
    

    Of course, this leads to the same problem: how do we randomly choose the position in the list? The computer doesn't really do anything randomly. So we improvise:

    #include <stdio.h>  /* printf      */
    #include <stdlib.h> /* rand, srand */
    #include <time.h>   /* time        */
    
    int main(void)
    {
      int i;
      
      srand(time(NULL));
      for (i = 0; i < 10; i++)
        printf("%i  ", rand());
        
      return 0;
    }
    Running the program 5 times gives these outputs: (compiled with gcc on Linux)
    1384334524  735471424  1860098722  694496962  1657646138  301995943  1803857288  1201859531  1067245105  2108720089
    2135987043  1232099519  337709210  1659609624  271785989  709050360  181970958  1810020192  440129707  1154963843
    1470269329  835711329  522547729  1580784891  2040547807  449856168  73845202  562074047  1973143356  89221058
    804551614  439323139  707386248  1501960159  1661825977  190661976  2113203095  1461611550  1358673356  1170961921
    954882118  1397642217  1261901805  1265485962  525660488  1560563048  1788825829  2012740412  1662747006  121217212
    
    A note about the time function:

    Note: Generally speaking, if you truly want a pseudo-random sequence, you should only seed the random number generator once at the beginning of the program (e.g. the first line in main), and never seed it again. If you seed it multiple times, you are actually biasing the sequence, causing the distribution of numbers to be less random.

    Putting a "Wrapper" Function Around rand()

    Although using the modulo operator, %, is simple and effective, it can be tedious and error-prone to use it to constrain the random numbers. A better solution is to write your own function that does all of the work for you.

    This simple function takes two parameters which specify a range (inclusive) that the random number should be within: (Plug in some numbers for low and high to convince yourself that the formula does work.)

    int RandomInt(int low, int high)
    {
      int number = rand() % (high - low + 1) + low;
      return number;
    }
    
    Examples:
    Random(10, 20)  --->  rand() % (20 - 10 + 1) + 10    --->  rand() % 11 + 10   --->  (0 to 10) + 10   --->  10 to  20
    Random(1, 100)  --->  rand() % (100 - 1 + 1) +  1    --->  rand() % 100 + 1   --->  (0 to 99) +  1   --->   1 to 100
    Random(-5, 5)   --->  rand() % (5 - -5 + 1) + -5     --->  rand() % 11 + -5   --->  (0 to 10) + -5   --->  -5 to   5 
    Random(1, 2)    --->  rand() % (2 - 1 + 1) + 1       --->  rand() % 2 + 1     --->  (0 to 1) + 1     --->   1 to   2
    Random(-15, -5) --->  rand() % (-5 - -15 + 1) + -15  --->  rand() % 11 + -15  --->  (0 to 10) + -15  --->  -15 to -5 
    
    Sample program:
    #include <stdio.h>  /* printf      */
    #include <stdlib.h> /* rand, srand */
    #include <time.h>   /* time        */
    
    int RandomInt(int low, int high)
    {
      int number;
      number = rand() % (high - low + 1) + low;
      return number;
    }
    
    int main(void)
    {
      int i;
      
      srand(time(0)); /* seed PRNG */
    
        /* 10 numbers between 10 and 20   */
      for (i = 0; i < 10; i++)
        printf("%3i  ", RandomInt(10, 20));
      printf("\n");
    
        /* 10 numbers between 100 and 200 */
      for (i = 0; i < 10; i++)
        printf("%3i  ", RandomInt(100, 200));
      printf("\n");
    
        /* 10 numbers between -10 and 10  */
      for (i = 0; i < 10; i++)
        printf("%3i  ", RandomInt(-10, 10));
      printf("\n");
    
      return 0;
    }
    Output from running the program 5 times:
     19   14   17   15   11   19   11   19   13   14
    138  140  140  106  134  115  118  182  155  101
      6   -9   -7   -7   -9    6    5    7    5    2
    
    
     12   16   16   14   11   14   16   13   14   18
    152  148  165  176  180  130  121  119  187  193
     -6   -7   -2   -6    2    4   -4    7    1    6
    
    
     12   14   10   16   13   12   17   13   19   12
    157  143  199  159  132  193  163  136  131  178
      9   -2   -2   10   -5   -9    5   -7    2   -2
    
    
     12   14   10   16   13   12   17   13   19   12
    157  143  199  159  132  193  163  136  131  178
      9   -2   -2   10   -5   -9    5   -7    2   -2
    
    
     20   14   18   18   10   19   15   19   20   17
    169  128  197  118  144  172  189  129  190  168
     -9   -8  -10   10   -2    6    2    4    6   -4
    

    Notice that the 3rd and 4th runs are the same. This is because the 4th run was run immediately after the 3rd run and the time hadn't changed.

    Interactive example:

    #include <stdio.h>  /* printf, scanf */
    #include <stdlib.h> /* rand, srand   */
    #include <time.h>   /* time          */
    
    int RandomInt(int low, int high)
    {
      int number;
      number = rand() % (high - low + 1) + low;
      return number;
    }
    
    int main(void)
    {
      int low, high, count, i;
    
      srand(time(NULL));
    
      printf("Enter lowest value: ");
      scanf("%d", &low);
    
      printf("Enter highest value: ");
      scanf("%d", &high);
    
      printf("How many numbers? ");
      scanf("%d", &count);
    
      for (i = 0; i < count; i++)
        printf("%i  ", RandomInt(low, high));
    
      printf("\n");
      return 0;
    }
    Output from running the program 4 times:
    Enter lowest value: 10
    Enter highest value: 20
    How many numbers? 10
    18  11  19  12  20  17  13  19  20  20
    
    
    Enter lowest value: -100 Enter highest value: 100 How many numbers? 100 54 -3 -95 -47 -98 -85 73 18 -67 -33 65 42 54 38 -9 18 62 91 91 11 47 10 35 -25 -70 98 -90 -71 33 58 19 -64 -46 -26 -61 -94 -11 -89 24 22 -22 -61 -36 82 77 55 -51 -12 -4 -60 50 -58 0 -66 18 30 -68 28 59 15 -65 -73 1 -11 1 40 -5 40 52 -81 -39 80 -92 -25 12 85 81 61 -78 -24 51 22 -32 -50 57 86 30 89 -36 -62 -47 -1 -85 54 39 -34 -57 -67 -95 95
    Enter lowest value: 0 Enter highest value: 50 How many numbers? 10 17 27 20 1 35 18 9 5 41 48
    Enter lowest value: 10 Enter highest value: 99 How many numbers? 200 94 70 31 18 17 61 66 31 57 23 18 99 70 73 17 67 35 93 45 99 99 47 23 82 23 51 24 68 71 45 62 28 15 45 36 75 96 92 96 53 68 66 14 90 91 21 57 26 15 92 77 14 91 90 86 15 93 63 35 65 98 49 83 66 84 71 41 42 63 89 47 83 18 51 73 61 63 30 40 30 74 17 34 28 60 73 85 53 36 20 70 86 59 53 14 95 24 45 37 40 87 36 75 57 39 49 18 54 31 48 74 58 56 99 76 68 72 23 21 60 33 82 46 44 87 51 39 64 48 28 56 87 16 31 44 45 70 53 90 54 91 26 12 99 25 40 67 49 53 41 99 38 75 46 72 24 49 63 40 87 81 86 75 87 18 71 84 40 24 36 84 68 53 48 67 68 78 87 70 83 80 69 21 55 67 45 31 68 98 62 18 79 48 45 28 18 16 65 11 31

    Here is an example of a simple pseudo-random number generator:

    Notes: