How does modulus and rand() work?

14,026

Solution 1

If C++11 is an option then you should use the random header and uniform_int_distrubution. As James pointed out in the comments using rand and % has a lot of issues including a biased distribution:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;

    std::mt19937 e2(rd());

    std::uniform_int_distribution<int> dist(6, 12);

    for (int n = 0; n < 10; ++n) {
            std::cout << dist(e2) << ", " ;
    }
    std::cout << std::endl ;
}

if you have to use rand then this should do:

rand() % 7 + 6

Update

A better method using rand would be as follows:

6 + rand() / (RAND_MAX / (12 - 6 + 1) + 1)

I obtained this from the C FAQ and it is explained How can I get random integers in a certain range? question.

Update 2

Boost is also an option:

#include <iostream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>

int main()
{
  boost::random::mt19937 gen;
  boost::random::uniform_int_distribution<> dist(6, 12);

  for (int n = 0; n < 10; ++n) {
    std::cout << dist(gen) << ", ";
  }
  std::cout << std::endl ;
}

Solution 2

You need rand() % 7 + 6.

Lowest number from rand() %7: 0. Highest number from rand() %7: 6.

0 + 6 = 6. 6 + 6 = 12.

Solution 3

The modulus operation a % b computes the remainder of the division a / b. Obviously the remainder of a division must be less than b and if a is a random positive integer then a%b is a random integer in the range 0 .. (b-1)


In the comments you mention:

rand()%(max-min)+min

This algorithm produces values in the half-open range [min, max). (That is, max is outside the range, and simply denotes the boundary. Since we're talking about ranges of integers this range is equivalent to the closed range [min, max-1].)

When you write '0 - 5' or '6 - 12' those are closed ranges. To use the above equation you have to use the values that denote the equivalent half open range: [0, 6) or [6, 13).

rand() % (6-0) + 0

rand() % (13-6) + 6

Note that rand() % (max-min) + min is just a generalization of the rule you've apparently already learned: that rand() % n produces values in the range 0 - (n-1). The equivalent half-open range is [0, n), and rand() % n == rand() % (n-0) + 0.

So the lesson is: don't confuse half-open ranges for closed ranges.


A second lesson is that this shows another way in which <random> is easier to use than rand() and manually computing your own distributions. The built-in uniform_int_distribution allows you to directly state the desired, inclusive range. If you want the range 0 - 5 you say uniform_int_distibution<>(0, 5), and if you want the range 6 - 12 then you say uniform_int_distribution<>(6, 12).

#include <random>
#include <iostream>

int main() {
  std::default_random_engine eng;
  std::uniform_int_distribution<> dist(6, 12);

  for (int i=0; i<10; ++i)
    std::cout << dist(eng) << " ";
}

rand()%7+6 is more terse, but that doesn't mean it is easier to use.

Solution 4

There are a couple of concepts here.

First, there's the range: [ denotes inclusive. ) denotes exclusive.

The modulus operator % n produces [0,n) - 0,..n-1

When you add a value to that result, you're adding the same value to both ends of the ranges:

%n + 6 = [n,n+6)

Now, if n is 6, as it in in your case, your output will be [0,6)

%6 + 6 = [6,12)

Now what you want is [6,13)

Subtract 6 from that:

[0,7) + 6 => n%7 + 6

Second, there is the matter of using rand(). It depends on whether you really care if your data is random or not. If you do really care that it is random, I highly suggest watching Stefan T Lavalej's talk on the pitfalls of using rand() at Going Native 2013 this year. He also goes into what you should use.

If the randomness of rand() doesn't matter to you, then by all means use it.

Share:
14,026
John
Author by

John

I am here to learn, to become a better programmer, but I'm also here to contribute with my knowledge.

Updated on July 14, 2022

Comments

  • John
    John almost 2 years

    So, I've been nuts on this.

    rand() % 6 will always produce a result between 0-5.

    However when I need between, let's say 6-12.

    Should I have rand() % 6 + 6

    0+6 = 6.
    1+6 = 7.
    ...
    5+6 = 11. ???
    

    So do I need to + 7 If I want the interval 6-12? But then, 0+7 =7. When will it randomize 6?

    What am I missing here? Which one is the correct way to have a randomized number between 6 and 12? And why? It seems like I am missing something here.

  • DanielKO
    DanielKO over 10 years
    @ktodisco: It's the XY problem again, better to just give the answer he needs instead of what he asked for.
  • kevintodisco
    kevintodisco over 10 years
    @DanielKO I think a better answer would explain the solution to the problem specifically being asked, and then give the advice of a better solution. It appears this answer has been edited to do so, though without explaining the former...