4

How I can get in C++ random value from 1 to 12?

So I will have 3, or 6, or 11?

Rolf ツ
  • 8,611
  • 6
  • 47
  • 72
MicheleIce
  • 49
  • 1
  • 1
  • 2
  • 17
    Did anyone else think `int Random(){ return 4; /*Guaranteed random -- Determined by dice roll */ }` – Earlz Feb 12 '10 at 19:25
  • I do think that the explanatory note is both superfluous and add ambiguity; there are other values between 1 and 12 (an infinite number in fact) – Clifford Feb 12 '10 at 19:35
  • I need this one zero before 3, or 4 to automatic filling in some application, so I need type in day: 01 not 1, and in months - 08 not 8 – MicheleIce Feb 12 '10 at 19:35
  • Ah, so your just needing to convert these numbers generated into a 2 digit fixed width number(as a string)? – Earlz Feb 12 '10 at 19:41
  • 7
    Yikes. The formatting of the values has nothing to do with the values. You can format them any way you want after you have generated them. – Tim Feb 12 '10 at 19:42

5 Answers5

16

Use the following formula:

M + rand() / (RAND_MAX / (N - M + 1) + 1), M = 1, N = 12

and read up on this FAQ.

Edit: Most answers on this question do not take into account the fact that poor PRN generators (typically offered with the library function rand()) are not very random in the low order bits. Hence:

rand() % 12 + 1

is not good enough.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • 2
    Using division gives only a minute improvement by itself -- the results *may* be more random, but they're still usually skewed. Assuming `rand()` produces all numbers up to RAND_MAX with equal probability, this will NOT produce numbers from 1 to 12 with equal probability, except in the (vanishingly rare) case that RAND_MAX happens to be a multiple of 12. – Jerry Coffin Feb 12 '10 at 19:37
  • 3
    +1 for raising the statistical bias issue from using the naive % approach. – Clifford Feb 12 '10 at 19:38
  • @Jerry: to be fair the FAQ linked gives other solutions. – Clifford Feb 12 '10 at 19:41
  • @Clifford: I hadn't checked the link, but you're right. Then again, if memory serves, I was partially responsible for what's in the FAQ... – Jerry Coffin Feb 12 '10 at 19:45
  • Actually, re-checking, it looks like I probably wasn't. For anybody who cares what I'm talking about, see: http://groups.google.com/group/comp.lang.c/browse_frm/thread/5a034f20e4981e53/8b4393f2ec2e40e7?hl=en#8b4393f2ec2e40e7 – Jerry Coffin Feb 12 '10 at 19:53
9
#include <iomanip>
#include <iostream>
#include <stdlib.h>
#include <time.h>

// initialize random seed
srand( time(NULL) );

// generate random number
int randomNumber = rand() % 12 + 1;

// output, as you seem to wan a '0'
cout << setfill ('0') << setw (2) << randomNumber;

to adress dirkgently's issue maybe something like that would be better?

// generate random number
int randomNumber = rand()>>4; // get rid of the first 4 bits

// get the value
randomNumer = randomNumer % 12 + 1;

edit after mre and dirkgently's comments

f4.
  • 3,814
  • 1
  • 23
  • 30
  • 2
    Poor method because the low-order bits of many random number generators are distressingly non-random. Read the FAQ I posted in my answer. – dirkgently Feb 12 '10 at 19:26
  • 2
    @dirkgently I bet this is just homework, so he doesn't much care if the numbers aren't truly random. – Earlz Feb 12 '10 at 19:27
  • +1 You were just too fast ;) Maybe change to std::cout – mre Feb 12 '10 at 19:28
  • 4
    @Earlz: Homework is about learning things right. Not copy-pasting. Hence, my answer does not have a full solution. A hint only. A very, very important hint to keep in mind. – dirkgently Feb 12 '10 at 19:31
  • 1
    Missing #include and it's std::setfill and std::setw – mre Feb 12 '10 at 19:36
4

Is there some significance to the leading zero in this case? Do you intend for it to be octal, so the 12 is really 10 (in base 10)?

Getting a random number within a specified range is fairly straightforward:

int rand_lim(int limit) {
/* return a random number between 0 and limit inclusive.
 */

    int divisor = RAND_MAX/(limit+1);
    int retval;

    do { 
        retval = rand() / divisor;
    } while (retval > limit);

    return retval;
}

(The while loop is to prevent skewed results -- some outputs happening more often than others). Skewed results are almost inevitable when/if you use division (or its remainder) directly.

If you want to print it out so even one-digit numbers show two digits (i.e. a leading 0), you can do something like:

std::cout << std::setw(2) << std::setprecision(2) << std::setfill('0') << number;

Edit: As to why this works, and why a while loop (or something similar) is needed, consider a really limited version of a generator that only produces numbers from, say, 0 to 9. Assume further that we want numbers in the range 0 to 2. We can basically arrange the numbers in a table:

0 1 2
3 4 5 
6 7 8 
9

Depending on our preference we could arrange the numbers in columns instead:

0 3 6  
1 4 7
2 5 8
    9

Either way, however, we end up with the one of the columns having one more number than any of the others. 10 divided by 3 will always have a remainder of 1, so no matter how we divide the numbers up, we're always going to have a remainder that makes one of the outputs more common than the others.

The basic idea of the code above is pretty simple: after getting a number and figuring where in a "table" like one above that number would land, it checks whether the number we've got is the "odd" one. If it is, another iteration of the loop is executed to obtain another number.

There are other ways this could be done. For example, you could start by computing the largest multiple of the range size that's still within the range of the random number generator, and repeatedly generate numbers until you get one smaller than that, then divide the number you receive to get it to the right range. In theory this could even be marginally more efficient (it avoids dividing the random number to get it into the right range until it gets a random number that it's going to use). In reality, the vast majority of the time, the loop will only execute one iteration anyway, so it makes very little difference what we execute inside or outside the loop.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • This seems correct to me, but it is not immediately obvious that it is. Could you elaborate on why it is correct, please? – Jørgen Fogh Mar 03 '11 at 14:09
1

You can do this, for example:

#include <cstdlib>
#include <cstdio>
#include <time.h>

int main(int argc, char **argv)
{
    srand(time(0));

    printf("Random number between 1 and 12: %d", (rand() % 12) + 1);
}

The srand function will seed the random number generator with the current time (in seconds) - that's the way it's usually done, but there are also more secure solutions.

rand() % 12 will give you a number between 0 and 11 (% is the modulus operator), so we add 1 here to get to your desired range of 1 to 12.

In order to print 01 02 03 and so on instead of 1 2 3, you can format the output:

printf("Random number between 01 and 12: %02d", (rand() % 12) + 1);
AndiDog
  • 68,631
  • 21
  • 159
  • 205
-2

(rand() % 12 + 1).ToString("D2")

Roo
  • 5
  • 3
  • 4
    The question is tagged C++, not C# or whatever that code is. – AndiDog Feb 12 '10 at 19:28
  • if someone can't figure out how to convert that to c++ then I question the ability to do any programming. – Tim Feb 12 '10 at 19:41
  • 2
    @tim:but at that point, it's basically not doing much more than echoing back the question. How do a print with two decimal places? Do the conversion with two decimal places! Oh, and along with that giving the same (bad) advice about how to reduce the range as at least half a dozen others... – Jerry Coffin Feb 12 '10 at 20:06