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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0xdead10cc
  • 521
  • 4
  • 6
  • 1
    Can't reproduce your problem in `C`. – Mat Oct 23 '11 at 14:42
  • 1
    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. – Lukman Oct 23 '11 at 14:47
  • 2
    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? – Omri Barel Oct 23 '11 at 14:48
  • Have you tried iterating more than once? I'm guessing that the first rand() value is always a multiple of 7. – Hot Licks Nov 17 '13 at 15:44
  • Based on the link provided by Grady Player, try importing ctime.h – Hot Licks Nov 17 '13 at 15:47
  • 1
    related: [Rand() % 14 only generates the values 6 or 13](https://stackoverflow.com/q/20263187/995714) – phuclv Aug 07 '18 at 08:03

2 Answers2

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
georg
  • 211,518
  • 52
  • 313
  • 390
  • 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. – gnometorule Oct 23 '11 at 18:12
  • It's unlikely though to make them simply remainders w/o other transformations first. – gnometorule Oct 23 '11 at 18:13
  • 2
    what a bad choice of LCG – phuclv Apr 18 '15 at 09:00
  • 6
    The part you left out: since `16807 % 7 == 0`, `(n * 16807) % 7 == 0` as well, as long as you didn't get integer overflow. – Mark Ransom Sep 24 '15 at 18:49
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++?.

Community
  • 1
  • 1
Grady Player
  • 14,399
  • 2
  • 48
  • 76
  • 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: – gnometorule Oct 23 '11 at 16:19
  • (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. – gnometorule Oct 23 '11 at 16:23
  • 1
    (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. – gnometorule Oct 23 '11 at 16:29
  • 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. – gnometorule Oct 23 '11 at 16:30