2

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.

JBentley
  • 6,099
  • 5
  • 37
  • 72
John
  • 2,900
  • 8
  • 36
  • 65
  • 4
    Do you have two hands nearby? Count on your fingers, how many numbers are there between 6 and 12? What do you think you need for the `%` operator? – DanielKO Oct 23 '13 at 22:13
  • 2
    Sure Daniel, mock me all you want, however I was trying the "algoritm" provided by another SO question which is rand()%(max-min)+min; Correct me If I am wrong, 12-6 = 6. min value is 6. So rand() % 6 + 6. That's why I am confused. – John Oct 23 '13 at 22:20
  • 2
    @John: It's a light-hearted way to tell you miss-counted it. You want seven different values, the argument you put on the right of `%` is how many different values you want to get. The algorithm you now mentioned is for a half-open interval (`[min, max)`); there are a few reasons to never use that (regarding both `rand()` and `%`); see Shafik's answer for the proper way to do it. Whoever told you to use `%` to generate random numbers within a range is probably too clueless to be instructing anyone. If it's a professor, feel free to send him the link to this question, we will straight him up. – DanielKO Oct 23 '13 at 22:26

4 Answers4

13

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 ;
}
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • @ktodisco: It's [the XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) again, better to just give the answer he needs instead of what he asked for. – DanielKO Oct 23 '13 at 22:30
  • @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... – kevintodisco Oct 24 '13 at 02:16
5

You need rand() % 7 + 6.

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

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

KlaFier
  • 159
  • 2
4

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.

bames53
  • 86,085
  • 15
  • 179
  • 244
1

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.

Carl
  • 43,122
  • 10
  • 80
  • 104