Why does rand() % 7 always return 0?
Asked Answered
E

2

39

This seems to be a really strange issue:

This is my code:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        srand((unsigned int)time(NULL));
        int newRandomNumber = 0;
        newRandomNumber = rand() % 7;
        NSLog(@"%d", rand() % 7); //This prints out what I expected
        NSLog(@"newRandomNumber = %d", newRandomNumber); // This always prints out 0!
    }
    return 0;
}

If I replace that one line that says

newRandomNumber = rand() % 7

with

newRandomNumber = rand() % 8

everything works perfectly. Why is that the case?

Evangelicalism answered 23/10, 2011 at 14:36 Comment(6)
Can't reproduce your problem in C.Yamamoto
I'm able to reproduce this in Objective-C. Even with srand([[NSDate date] timeIntervalSince1970]). But passing smaller integer to srand() seems to fix it.Doublespace
What do you get if you print rand()? Do you get the same number each time, or only something that is congruent to 0 modulo 7?Debit
Have you tried iterating more than once? I'm guessing that the first rand() value is always a multiple of 7.Roer
Based on the link provided by Grady Player, try importing ctime.hRoer
related: Rand() % 14 only generates the values 6 or 13Felspar
P
42

Well, this

int seed;
for(seed = 1; seed < 10; seed++) {
    srand(seed);
    printf("%4d %16d\n", seed, rand());
}

prints

   1            16807
   2            33614
   3            50421
   4            67228
   5            84035
   6           100842
   7           117649
   8           134456
   9           151263

which makes me think that rand() = seed * 16807

Wikipedia article Linear congruential generator confirms that CarbonLib indeed uses Xn+1 = Xn * 16807 to generate random numbers.

Pebbly answered 23/10, 2011 at 17:34 Comment(4)
Formula on Linux 2.6.32 I run is more complicated. It's of the form next = ( next * const_1 + const_2) % 32,768. obviously, maybe you reverse-engineered your platform's formula. It's implementation dependent though with constants that often are near-prime after twisting and turning them.Jari
It's unlikely though to make them simply remainders w/o other transformations first.Jari
what a bad choice of LCGFelspar
The part you left out: since 16807 % 7 == 0, (n * 16807) % 7 == 0 as well, as long as you didn't get integer overflow.Pique
S
9

It seems unlikely but running some tests, after an srand the first rand seems always to be divisible by 7, at least in an int sized variable.

On several runs I got 1303562743, 2119476443, and 2120232758, all of which mod 7 to 0.

The second rand() works, because it is the second rand(). Throw a rand() before your first rand()... or better yet, use a better random number generator random or arc4rand if available.

Also see Stack Overflow question Why is (rand() % anything) always 0 in C++?.

Shelah answered 23/10, 2011 at 15:41 Comment(4)
Grady player pretty much answers your question: this is because it is your first call to a number not truly randomly generated. However, one other point and one fact that still puzzles me:Jari
(1) if you don't see with stand() at all, the system will always produce the sane number on first call. On my system, a 'next' variable keeps track of the next rand() value, and next is seeded to 1 if no srand() call happens. The value reported back is after adding and multiplying system constants and taking the result mod 32,768. This means you would still get 0 for a certain modulus user chosen on first call, in your case 7, but it could be another number.Jari
(2) the article talks about portability issues, which wouldn't be affected by using a modulus. Essentially it points it that time_t might not be properly castable to an unsigned int; and that it is better to not use mod, instead multiply to get your desired range (in your case, *7/RAND_MAX). As this objective C which I don't know, it might be an issue, but I doubt it. What puzzles me is that the time() value reported back gets updated internally at every global timer interrupt in update_tines() (on the LINUX 2.6.xx kernel in any case), and this should happen way between second ticks.Jari
Why srand() would report back, as you say it does, essentially what amounts to 7 second increments (I know this isn't precisely what it means, but it seems related), makes me scratch my head.Jari

© 2022 - 2024 — McMap. All rights reserved.