Sign in to follow this  
Kalner

Getting Random Numbers into a Variable

Recommended Posts

So today I found out how to get a sort of truly random number with seeding it with the computers time but I dont know how to get that number into a variable. I was thinking of making a guess the number game. Here is my code.
[code]#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
srand(time(0));
cout << rand() <<endl;
}
[/code]

Share this post


Link to post
Share on other sites
Since [url=http://www.cplusplus.com/srand]srand[/url] takes a single parameter that set the seed of the PRNG, then [url=http://www.cplusplus.com/reference/clibrary/ctime/time/]time[/url] is the function that retrieves the system's computer time. From here it becomes simple, first declare a varaible then store it:
[code]
time_t t = time(0);
srand( (unsigned int)t );
cout << "The seed is: " << t << endl;
[/code]

Share this post


Link to post
Share on other sites
I think that the op was asking how to get the random number into a variable. if i am wrong ignore this message.

you would use

[code]
srand(time(0));
int number = rand();
[/code]

if you are making a number guessing game you would prolly want to lower the possible numbers since rand() can generate numbers like 32000 or higher, to do that you would need to make a function for that. I got mine form the book Accelerated C++.

[code]
#include <cstdlib>
#include <stdexcept>

using namespace std;

int nrand(int n)
{
if (n <=0 || n > RAND_MAX)
throw domain_error("Argument to nrand is out oof range:")

const int bucket_size = RAND_MAX / n;
int r;

do r = rand() / bucket_size;
while (r >= n);

return r;
}
[/code]

you would use it like:

[code]srand(time(0));
int number = nrand(101);[/code]

this would make the variable number a random nubmer from 0 - 100.

Share this post


Link to post
Share on other sites
One simple way of generating a random number from A to B (exclusive) is to use a function like this:
[code]
int rand(int begin, int end)
{
assert(begin < end);
int range = end - begin;
return begin + (std::rand() % range);
}
[/code]
For small ranges, the above can be biased. For instance, a similar implementation (in C#) I tested in a [url="http://www.gamedev.net/topic/619146-random-numbers-help/page__view__findpost__p__4906371"]recent thread[/url] had a significant bias towards odd numbers.

An alternative is the following:
[code]

int rand(int begin, int end)
{
assert(begin < end);
float r = std::rand() / static_cast<float>(RAND_MAX);
int range = end - begin;
return begin + static_cast<int>(r * range);
}
[/code]

Share this post


Link to post
Share on other sites
[quote name='moneal2001' timestamp='1329591870' post='4914277']
you would use it like:

[code]srand(time(0));<br>int number = nrand(101);[/code]

this would make the variable number a random nubmer from 0 - 100.
[/quote]

I was thinking of making several difficultys and they can range from easy = 1-10 medium = 1-25 hard = 1-50. A switch statement can work for that but how do I make it from 1-10 not 0-100?

EDIT: [code]#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
srand(time(0));
cout << 1+(rand()%10) <<endl;
[/code]

This code seemed to do the trick from getting numbers from 1-10.

Thanks alot guys.

Share this post


Link to post
Share on other sites
[quote name='Kalner' timestamp='1329605309' post='4914324']
int main()
{
srand(time(0));
cout << 1+(rand()%10) <<endl;
[/code]

This code seemed to do the trick from getting numbers from 1-10.
[/quote]

That's good enough for practice or any "non-serious" use, but as rip-off pointed out, you will NOT have the same probability for all numbers (though depending on your range).

A quick and dirty test of how unequally distributed your numbers are would be something like:

[code]
int counts[10] = {0};
int samples = 1000000;
while (samples-- > 0)
++counts[ rand() % 10 ];

std::copy(counts, counts+10, ostream_iterator<int>(cout, " "));
/* Yes, you could just do for (int i=0; i<10; ++i) cout << counts[i] << " "; */
cout << endl;
[/code]

If you run this multiple times and any of the values is consistently and significantly larger than others, you already know which number you should probably always guess first.

Share this post


Link to post
Share on other sites
To make it from 1 - 10 you would do as follows :

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {

srand(time(0));
int randomNumber = rand() % 10 + 1;

return 0;
}

the line: int randomNumber = rand() % 10 + 1; is going to get a rundom number between 0 and 9 (with any positive number divided by 10, the remander will be between 0 and 9) and then you add 1. so if the number is 0, it will become 1, and if it is 9, it becomes 10.


hopefully this helps [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

Share this post


Link to post
Share on other sites
[b]Hi![/b]

For Windows development is possible to use such API:

(An example is written with a ULONG_PTR keyword, but the desired type would be replaced.)

[code]#define SystemFunction036 NTAPI SystemFunction036
#include <Ntsecapi.h>
#undef SystemFunction036

ULONG_PTR GetRandomULongPtr( const ULONG_PTR pMin, const ULONG_PTR pMax )
{
ULONG_PTR randomValue;
RtlGenRandom(
&randomValue,
sizeof( randomValue ) );
return pMin + randomValue % ( pMax - pMin + 1 );
}
[/code]

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1329598379' post='4914300']
One simple way of generating a random number from A to B (exclusive) is to use a function like this:
[code]
int rand(int begin, int end)
{
assert(begin < end);
int range = end - begin;
return begin + (std::rand() % range);
}
[/code]
For small ranges, the above can be biased. For instance, a similar implementation (in C#) I tested in a [url="http://www.gamedev.net/topic/619146-random-numbers-help/page__view__findpost__p__4906371"]recent thread[/url] had a significant bias towards odd numbers.

An alternative is the following:
[code]

int rand(int begin, int end)
{
assert(begin < end);
float r = std::rand() / static_cast<float>(RAND_MAX);
int range = end - begin;
return begin + static_cast<int>(r * range);
}
[/code]
[/quote]
That doesn't remove the bias at all though, it just attempts to hide it. Specifically rather than the lower outcomes having a higher probability, the same number of values have a similarly slightly higher probability but the ones that do are spread across the range.
E.g. The best way to examine such a thing is to pretend you have a much smaller random number of bits. Say you had a 4-bit random number generator and you wanted to generate a number from 0-5, (perhaps for a die roll where you add 1 afterwards to get 1-6).

Using the basic mod method you would get[list]
[*]0 if rand() was 0, 6, or 12
[*]1 if rand() was 1, 7, or 13
[*]2 if rand() was 2, 8, or 14
[*]3 if rand() was 3, 9, or 15
[*]4 if rand() was 4 or 10
[*]5 if rand() was 5 or 11
[/list]
With the code involving the conversion to and from floating point you get:[list]
[*]0 if rand() was 0, 1, or 2
[*]1 if rand() was 3, or 4
[*]2 if rand() was 5, 6, or 7
[*]3 if rand() was 8 or 9
[*]4 if rand() was 10, 11, or 12
[*]5 if rand() was 13 or 14,
[*][b]6 if rand() was 15 (bug!!!)[/b]
[/list]
Note the bug I even found upon actually using the floating point method above, to map inputs to outputs. That was with a range of 5. Using a range of 6 all except the last outcome come from 3 possible inputs and the last only comes from one possible input, also very biased.

The problem is that you still have the same number of inputs going to the same number of outputs. So you at best only manage to sweep the bias under the rug.

Here is the rejection method, which I've previously posted on another site. It completely solves the bias problem:
[code]// Generates random number between min and max, inclusive.
int random(int min, int max)
{
int range, result, cutoff, divisor;
if (min >= max)
return min; // only one outcome possible, or invalid parameters
range = max-min+1;
divisor = RAND_MAX / range;
cutoff = divisor * range;
// Rejection method, to be statistically unbiased.
do {
result = std::rand();
} while (result >= cutoff);
return result / divisor + min;
}[/code]Note that this is a slight improvement over what I've previously posted on a different site, where this version also helps avoid the slight lack of randomness in the lower bits of rand.

Back to the 4-bit generator example. With the plain old mod method notice how there are three ways to get 0-3 but only two ways of getting 4-5. This is statistically biased towards the lower numbers! So the rejection method would throw away any random number of 12 or above and try again. That way there are exactly two ways of arriving at each outcome. (The calculation for cutoff calculates 12 in this case due to the integer division result of 15 / 6 being 2, and 2 * 6 = 12)

With the rejection method above you get:[list]
[*]0 if rand() was 0 or 1
[*]1 if rand() was 2 or 3
[*]2 if rand() was 4 or 5
[*]3 if rand() was 6 or 7
[*]4 if rand() was 8 or 9
[*]5 if rand() was 10 or 11
[/list]
All outcomes are now equally likely.

About the loop:
Here it is ignoring 4 values out of 16 so it will loop around again 1/4th of the time, but in a real situation it loops so few times you do not have to worry about it. E.g. when getting a random number for a die roll using random number generator that gives a 15-bit output, it loops only 4 out of 32767 times. That's small enough that you don't need to worry about it. The chance of that looping even 4 times is 0.000000000018%

Last but not least, make sure that you call srand only once in your program, preferably inside main.

Oh and one final note, on Windows, the ultimate random number generator is CryptGenRandom. :-)

Share this post


Link to post
Share on other sites
[quote]
[left]With the code involving the conversion to and from floating point you get:[/left][list]
[*]0 if rand() was 0, 1, or 2
[*]1 if rand() was 3, or 4
[*]2 if rand() was 5, 6, or 7
[*]3 if rand() was 8 or 9
[*]4 if rand() was 10, 11, or 12
[*]5 if rand() was 13 or 14,
[*][b]6 if rand() was 15 (bug!!!)[/b]
[/list]
[/quote]
Hmm? For the case rand() is 15, then r = 1.0f. The result is begin + (r * range), where range is 5. So the result would be 5 in this case, not 6. Am I wrong?

In fact, this is the output I get:[list]
[*]0 if rand() is 0, 1 or 2
[*]1 if rand() is 3, 4 or 5
[*]2 if rand() is 6, 7 or 8
[*]3 if rand() is 9, 10 or 11
[*]4 if rand() is 12, 13 or 14
[*]5 if rand() is 15
[/list]
Now, we still have a problem because 5 is significantly less likely to appear. So your correction is warranted.

[quote]
[left]With the rejection method above you get...[/left]
[/quote]
Ok, that is indeed better.

I was thinking of the wrong problem, I believe a corrected version of the function I provided could be used to extend the range of values above RAND_MAX (without calling rand() multiple times). E.g. We want a potentially very large float or double, but we're happy to accept a sample of RAND_MAX values in that range (as opposed to mandating that any value in the range could appear).

[quote]
Oh and one final note, on Windows, the ultimate random number generator is CryptGenRandom. :-)
[/quote]
For applications requiring cryptographic random numbers, sure.

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1329731676' post='4914785']
[quote]

[left]With the code involving the conversion to and from floating point you get:[/left][list]
[*]0 if rand() was 0, 1, or 2
[*]1 if rand() was 3, or 4
[*]2 if rand() was 5, 6, or 7
[*]3 if rand() was 8 or 9
[*]4 if rand() was 10, 11, or 12
[*]5 if rand() was 13 or 14,
[*][b]6 if rand() was 15 (bug!!!)[/b]
[/list]
[/quote]
Hmm? For the case rand() is 15, then r = 1.0f. The result is begin + (r * range), where range is 5. So the result would be 5 in this case, not 6. Am I wrong?[/quote]Yeah that makes sense. I think I got the results mixed up, those buggy results were with a range of 6, and the other results were from a range of 5.

Share this post


Link to post
Share on other sites
If you want to use standard C++ instead of pure 1970s-era C, you could also use the following code. (which prints out 10 randomly generated integers between one and ten inclusive).
[code]
#include <ctime>
#include <iostream>
#include <random>

int
main(int, char**)
{
std::default_random_engine prng(std::time(NULL));
std::uniform_int_distribution<> generate(1, 10);

for (int i = 0; i < 10; ++i)
{
std::cout << i << ": " << generate(prng) << "\n";
}
}
[/code]

Share this post


Link to post
Share on other sites
[quote name='Bregma' timestamp='1329771316' post='4914943']
If you want to use standard C++ instead of pure 1970s-era C, you could also use the following code. (which prints out 10 randomly generated integers between one and ten inclusive).[/quote]I should note that my original code for this was posted to a C forum, it was not really intended for C++.

Thanks for the C++ example. It makes sense that a common enough thing that is gotten wrong often enough, would have been made part of C++ by now.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this