0

I have written this piece of code which is supposed to simulate throwing dice many many times and counting that how many times each face is up. I have attached the output down there and as you can see it looks kind of strange. For example face 5 comes up exactly 10 times, faces 2, 3, 4 are about the same and face 6 comes zero in two rounds. The only face which acts about normal is 1. Can anyone explain this to me? Is this normal? Am I doing something wrong or is it something related to my system?

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

int main(void) {

    unsigned long int freq1, freq2, freq3, freq4, freq5, freq6;
    unsigned long int L00p = 1;
    unsigned short int DF;

    while (L00p <= 6e7){
        srand (time(NULL));
        DF = 1 + (rand ()%6);
        switch (DF)
        {
        case 1:
            ++freq1;
            break;
        case 2:
            ++freq2;
            break;
        case 3:
            ++freq3;
            break;
        case 4:
            ++freq4;
            break;
        case 5:
            ++freq5;
            break;
        case 6:
            ++freq6;
            break;
        default:
            break;}
        ++L00p;
    }

    printf ("%s%25s\n", "Dice's Face", "Face Frequency");
    printf ("1%25lu\n", freq1);
    printf ("2%25lu\n", freq2);
    printf ("3%25lu\n", freq3);
    printf ("4%25lu\n", freq4);
    printf ("5%25lu\n", freq5);
    printf ("6%25lu\n", freq6);


    return 0;
}

and here is the program's output after four times running it:

Programme's output

Rachid K.
  • 4,490
  • 3
  • 11
  • 30
Vynylyn
  • 172
  • 11
  • 7
    Call `srand()` *once only*. You keep resetting the sequence. Aside: do you know about arrays? You can use an array to keep track of the counts. – Weather Vane Sep 13 '22 at 08:04
  • 4
    Also you have not initialised the frequency counters.You must set them to `0` at the beginning. – Weather Vane Sep 13 '22 at 08:07
  • 2
    You could improve this code a lot by using an array instead of six separate integers, e.g. `unsigned long int freqs[6] = { 0, 0, 0, 0, 0, 0 }; srand (time(NULL)); for (int i=0; i<60000000; i++) { freqs[rand()%6]++; }` – r3mainer Sep 13 '22 at 08:08
  • 1
    `while (L00p <= 6e7)`? Typo? – anastaciu Sep 13 '22 at 08:10
  • @WeatherVane In the first edition of this code all of those counters were initialized to zero. That's what I usually do when writing a code. But that way output was even worse. because in each run at least 3 or 4 counters finished zero. – Vynylyn Sep 13 '22 at 08:13
  • @WeatherVane No, 'haven't been taught of arrays, yet. – Vynylyn Sep 13 '22 at 08:14
  • @r3mainer No, 'haven't been taught of arrays, yet. – Vynylyn Sep 13 '22 at 08:15
  • @Vynylyn not initializing `freqn` to zero is definitely wrong. – Jabberwocky Sep 13 '22 at 08:25
  • 1
    @Vynylyn and you definitely should read this: https://stackoverflow.com/questions/7343833/srand-why-call-it-only-once. If you call `srand` in the loop, the sequence of numbers you'll get from `rand` will definitely not be random. – Jabberwocky Sep 13 '22 at 08:26

2 Answers2

2
  • You don't initialize the frequency counters, so they'll likely contain garbage from the stack. (So yes, you were getting randomness, but not the randomness you want.)
  • You don't want to call srand() in the loop, but only once before it. Calling srand() with the same number (and time(NULL) will quite inevitably return the same second in a tight loop) will reset the rand() generator to return the same sequence of numbers, and since you only ever call rand() once before calling srand() again, you'll get a whole bunch of the same number.

The following version works fine, but you'd have a better time with an array.

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

int main(void) {
  unsigned long int freq1 = 0, freq2 = 0, freq3 = 0, freq4 = 0, freq5 = 0,
                    freq6 = 0;
  srand(time(NULL));

  for (int loop = 0; loop < 1000; loop++) {
    int DF = 1 + (rand() % 6);
    switch (DF) {
    case 1:
      ++freq1;
      break;
    case 2:
      ++freq2;
      break;
    case 3:
      ++freq3;
      break;
    case 4:
      ++freq4;
      break;
    case 5:
      ++freq5;
      break;
    case 6:
      ++freq6;
      break;
    default:
      break;
    }
  }

  printf("%s%25s\n", "Dice's Face", "Face Frequency");
  printf("1%25lu\n", freq1);
  printf("2%25lu\n", freq2);
  printf("3%25lu\n", freq3);
  printf("4%25lu\n", freq4);
  printf("5%25lu\n", freq5);
  printf("6%25lu\n", freq6);

  return 0;
}

Example output:

Dice's Face           Face Frequency
1                      177
2                      160
3                      166
4                      169
5                      155
6                      173

For reference, a version using an array of 6 ints:

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

int main(void) {
  unsigned long int freqs[6] = {0};
  srand(time(NULL));

  for (int loop = 0; loop < 1000; loop++) {
    freqs[rand() % 6] ++;
  }

  printf("%s%25s\n", "Dice's Face", "Face Frequency");
  for(int face = 0; face < 6; face++) {
    printf("%d%25lu\n", face + 1, freqs[face]);
  }

  return 0;
}
AKX
  • 152,115
  • 15
  • 115
  • 172
1

Here is an annotated adaptation of your code for educational purposes. You've learned about "loops", so here is an application for a do/while() loop.

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

// Global variables are frowned upon because as the code grows more complex
// it is difficult or impossible to see where a value may be changed (inappropriately)

// For a tiny program like this that is unlikely to grow
// this proves the global variables are, by default, initialised to zero.

unsigned long int freq1, freq2, freq3, freq4, freq5, freq6;

int main() {
    unsigned long int L00p = 0; // "local" var initialised. Good!

    srand( time( NULL ) ); // called once at start of program.

    do {
        switch( ( rand() % 6 ) ) { // braces consistent with your main()
            case 1: ++freq1; break; // get used to base-0 counting
            case 2: ++freq2; break;
            case 3: ++freq3; break;
            case 4: ++freq4; break;
            case 5: ++freq5; break;
            case 0: ++freq6; break; // Ha-ha!! modulo!!!
            default: printf( "A miracle has happened!!\n" );
                break;
        } // DO NOT hide that closing brace as you did. Prominent!
    } while( ++L00p < 6e3 ); // increment counter after each loop done

    // Swapped your output columns
    // Using one format specifier for header and one for counts
    // Notice how easy to modify only one instance?
    char *tFmt = "%9s : %s   Loops = %d\n";
    char *oFmt = "%9lu : %d\n";

    printf( tFmt, "Frequency", "Face", L00p );

    // and... why not???
    for( L00p = 0; L00p < 6; L00p++ ) {
        int n; // not init'd because used immediately
        switch( L00p ) {
            case 1: n = freq1; break;
            case 2: n = freq2; break;
            case 3: n = freq3; break;
            case 4: n = freq4; break;
            case 5: n = freq5; break;
            case 0: n = freq6; break;
        }
        printf( oFmt, n, L00p + 1 );
    }

    return 0;
}

Output

Frequency : Face   Loops = 6000
      958 : 1
     1038 : 2
     1018 : 3
     1031 : 4
      956 : 5
      999 : 6

Again, for a simple, small piece of code like this, being able to see the entire switch block and compare values at a glance, concatenating statements can AID in writing bug-free code.

Fe2O3
  • 6,077
  • 2
  • 4
  • 20
  • 1
    Dear Iron-3 oxide, thanks for sharing this. This shows difference between an XPed programmer and a newbie. The way you shortened my code and compress that Switch-statement was nice. Althought, I do not understand what is `char *Fmt` or `char *oFmt` I mean the whole `char *` thing. I thought that char type can only hold one character (or its integer equivalent) now I can see that you've put a whole line into it. Kind of confused. Maybe these are topics I haven't been told of yet and I'd better wait till I learn them later. – Vynylyn Sep 13 '22 at 09:37
  • 1
    @Vynylyn That '*' is the star of C. Truly!! It means "oFmt", for instance, **points** to a character. In this instance oFmt is pointing to the first character in the _null terminated array of characters_ that is a "string" (you've probably heard of those.) Some people find pointers difficult, but just be clear that a pointer IS a variable that contains the **address** of another variable. They are powerful when you master the concept (and it is easy enough if you, at first, draw things on paper to keep it all straight.) Best wishes for your adventures in coding! `:-)` "rust never sleeps" :-) – Fe2O3 Sep 13 '22 at 09:46
  • 1
    @Vynylyn Consider a universe (or world) in which dice have one blank face (0) and 5 other faces with the appropriate number of dots... Programming would be so much easier! `:-)` In fact, I've just altered the code to do JUST THAT!! Fun, eh??? This'll get chins wagging `:-)` – Fe2O3 Sep 13 '22 at 09:52
  • The "clever" modulo addition isn't necessary IMO; the fact `rand() % 6` returns 0..5 doesn't make a difference for the logic or printout, and the switch cases not being in the usual order has the reader going "wait, why is this like this, is there something special going on here"... – AKX Sep 14 '22 at 05:15