0

I'm learning C and I want to generate a number between 0 and 6400. This is the code I came up with:

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

int main()
{
    srand(time(0));
    int i = (rand() % 6401);
    printf("Random number between 0 and 6400: %d\n", i);
    return 0;
}

When I compile and run this code from the command line I get some very weird results:

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6282

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6282

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6285

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6285

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6289

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6289

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6292

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6292

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6295

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6298

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6298

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6302

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6302

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6305

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6305

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6308

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6308

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6311

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6311

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6315

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6315

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6318

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6318

K:\C\Labo\Oefeningen 2019>a
Random number between 0 and 6400: 6321

K:\C\Labo\Oefeningen 2019>

The numbers are all different but I would expect a somewhat even distribution between 0 and 6400. The weird thing is that I was using the same function without problems an hour ago? (I was using it to generate smaller numbers before.) I'm certain it's something really stupid that I'm missing but I've been stuck for an hour now.

EDIT: I know It will give the same value when you run the code within the same second. I waited multiple seconds (10-20) between executions and I still get the same result? The values are rarely the same, they are just very very very similar 100 % of the time. How do I get around this?

Zwettekop
  • 93
  • 7
  • 2
    `time(0)` returns the number of seconds elapsed since 1970. Its value should change every second. So if you run this program more than once within a second, you may get the same results. But once time passes, the values should not be the same. You can add the output of that function to the console for comparison. If you need a fraction of a second, you can use `gettime`, which requires a struct pointer as a parameter. – Andrew Sep 28 '19 at 22:47
  • 1
    Possible duplicate of [srand(time(0)) and random number generation](https://stackoverflow.com/q/4736485/608639), [Understanding srand(time(0)) behaviour](https://stackoverflow.com/q/49479096/608639), [srand(time(0)) not making random numbers?](https://stackoverflow.com/q/22749167/608639), etc. – jww Sep 28 '19 at 23:02

5 Answers5

3

time() has resolution of 1 second. So your program will generate at different value only after half a second has passed on average.

If your compiler supports C11, you can use a higher-resolution function, timespec_get(). Your srand(time(0)); will then transform to the following:

struct timespec ts;
timespec_get(&ts, TIME_UTC);
srand(ts.tv_nsec);

Here ts.tv_nsec is the nanosecond part of the time stamp, whose resolution should be good enough for your purpose.

If your compiler doesn't support C11, you can still have a better source of random seed than time(), with about a millisecond resolution (actual resolution is given by CLOCKS_PER_SEC macro): the clock() function. Then your seeding code will be

srand(clock());

Note though that it may actually be a bad source of entropy, especially if your OS is not busy, so that the program would run at somewhat predictable pace. It's because beginning of the clock()'s era is related to the program's execution, not to real time. It might be better to e.g. use a sum of clock() and time(0) to get more unpredictable value:

srand(time(0)+clock());
Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • I waited multiple seconds between execution but the value still lies between 6280 and 3000. To see any real change I have to wait minutes. Why is that? – Zwettekop Sep 28 '19 at 23:25
  • @ZwarteKop Might be that the implementation of `srand` and `rand` in your compiler is too bad. Try running `srand((unsigned)rand()*rand())` after the first `srand`: this might give some better results, although I don't know if it will. – Ruslan Sep 28 '19 at 23:28
  • Nice idea with the `ts.tv_nsec` part; this will essentially be totally random and goes through a full cycle every 1s. You might consider adding `getpid()` to that if a niche case arises. – S.S. Anne Sep 28 '19 at 23:31
  • 1
    @JL2210 that's POSIX, not pure C. – Ruslan Sep 29 '19 at 06:52
0

Computers doesn't really generate random numbers. Thus, when you execute your code twice on the same second it returns the same value. In order to have better result you could add the value of getpid() in srand.

Keep in mind that this still not real random.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
elkolotfi
  • 5,645
  • 2
  • 15
  • 19
0

If your rand is busted, you can try one of the xorshift pseudo-random number generators. They are not perfect but the resulting implementation is very short. It could be enough for your own use.

Here is an implementation example: I used this one as reference.

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

uint64_t    xorshift64s(uint64_t seed)
{
    static uint64_t i = 1;

    if (seed != 0)
        i = seed;
    i ^= i >> 12;
    i ^= i << 25;
    i ^= i >> 27;
    i *= 0x2545f4914f6cdd1d;
    return (i >> 32);
}


int main()
{
    srand(time(0));
    int i = (rand() % 6401);
    printf("rand    : Random number between 0 and 6400: %d\n", i);
    xorshift64s(time(0));
    int j = (xorshift64s(0) % 6401);
    printf("xorshift: Random number between 0 and 6400: %d\n", j);
    return 0;
}
AugustinLopez
  • 348
  • 1
  • 3
  • 13
0

If anyone else runs into this problem I think I found a workaround. I know this isn't a perfect solution but it's the only one that worked for me. I think the random number generator used in my compiler doesn't like similar seeds at all. With this code snippet it actually generates somewhat acceptable semi-random numbers:

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

int main()
{
    srand((unsigned int)time(0) * 100000000);
    int i = (rand() % 6401);
    printf("Random number between 0 and 6400: %d\n", i);
    return 0;
}

This is where I got my garbage can compiler from btw: http://tdm-gcc.tdragon.net/download

Zwettekop
  • 93
  • 7
0

The numbers generated by rand aren't truly random, they're generated with a formula. That's why seeding is both possible and necessary. Depending on the formula used, there can be a high correlation between the seed and the first few random numbers.

The cures are to use a better formula (something not rand), use a more random seed, or waste a few random numbers just after seeding.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622