Random Number

Pages: 12
How do I create a single random number that is either less than 0 or greater than 10?

 
  int x = rand() || rand()


Something like this?
Last edited on
Any number less than 0 and greater than 10?
Last edited on
Correct. But needs to be random.
Last edited on
A random number must follow some distribution. rand() is bounded between [0, RAND_MAX].
It can't be unbounded in the manner you say, (-infinity, 0) ∪ (10, infinity).
(Well, technically there is a way to turn a continuous uniform distribution into another distribution, but I think that would overly complicated, and you'd have lots of gaps because of rand() discrete values.)

A Gaussian (normal) distribution output is unbounded, but its probability density makes the chance of generating a number outside a certain distance from the mean increasingly small.

But technically the following could do what you want... but I think that your question is ill-formed, because you don't have a rigid bounds or mention a distribution. (What do you want to do when the number 1,000,000,000 is generated?)

https://ideone.com/5zsiQo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>

#include <random>
#include <cmath>

int custom_random_number(int min_excluded, int max_excluded)
{
    static std::random_device rd;
    static std::mt19937 gen(rd());
 
    // values near the mean are the most likely
    // standard deviation affects the dispersion of generated values from the mean
    double mean = (max_excluded - min_excluded) / 2.0;
    double variance = 100 * std::abs(max_excluded - min_excluded); // correlates to how "wide" you want the distribution to be
    std::normal_distribution<> d(mean, variance);
    int r;
    do {
        r = std::round(d(gen));
    } while ( r >= min_excluded && r <= max_excluded );
    return r;
}

#include <map>

#include <iomanip>

int main()
{
    int r = custom_random_number(0, 10);
    std::cout << r << "\n" << std::endl;;;
    
    std::map<int, int> hist;
    for(int n = 0; n < 10000; n++) {
        ++hist[custom_random_number(0, 10)];
    }
    for(auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second, '*') << '\n';
    }
}

cpp.sh is dumb and doesn't like my code, use the ideone link and not the gear symbol.

example output, notice no numbers in [0, 10] range
1
2
3
4
5
6
7
8
9
10

...
-4 **
-3 ***
-2 ***
-1 *
11 ****
12 
13 **
...

The * is the number of times that number was randomly chosen.
Last edited on
Ahh I see, thanks.

But if i had the same question and say I wanted it to be >10 and <20,
could I not just get a number below 10 and subtract 11 from it, to make it negative?

I guess the same code would work above for the range, but is there a different way?
Last edited on
If you want to make an inclusive bound like between [10, 20] (inclusive), that's a lot easier.

Here's a possible implementation of what I think you mean:
https://ideone.com/S8CV2t

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <random>

#include <map>
#include <iomanip>

int main()
{

  std::default_random_engine generator;
  const int Min = 10;
  const int Max = 20;
  std::uniform_int_distribution<int> distribution(Min, Max);

  const int nrolls = 10000; // number of experiments
  
  // generate numbers, make a histogram of distribution
  std::map<int, int> histogram;
  for (int i = 0; i < nrolls; i++) {
    int random_number = distribution(generator); // here's where # is generated
    ++histogram[random_number ];
  }

  // print the histogram
  for (auto p : histogram) {
    std::cout << std::fixed << std::setprecision(1) << std::setw(2)
              << p.first << ' ' << std::string(p.second/(nrolls/(10*(Max-Min))), '*') << '\n';
  }
  return 0;
}
Last edited on
Thanks.

The first way was along the lines of what I was looking for.

I want a number less than 10 OR greater than 20.
But a RANDOM number on either side of those limits.
Is there a limit in your design on how low the lowest random number should be, and how high the highest random number generated should be?


The first way was along the lines of what I was looking for. I want a number less than 10 OR greater than 20.

Indeed, my first post can do that custom_random_number(10, 20);

You should note though that the implementation in my first post has nondeterministic runtime -- technically, there's an astronomically small chance that the generator could keep generate 5 for an hour of running inside the while loop.

Here's an implementation that has a deterministic runtime, and is bounded between -100 and 100, inclusive.
https://ideone.com/JpWMYK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <random>

#include <map>
#include <iomanip>

int keep_out_of_range(int low, int high, int number)
{
	if (number >= low && number <= high)
	{
		int halfway = (low + high) / 2;
		int halfrange = (high - low) / 2;
		if (number > halfway)
			number += halfrange;
		else
		{
			number -= halfrange + 1;
		}
	}
	return number;
}

int main()
{
  const int MinGen = -100;
  const int MaxGen =  100;
  std::default_random_engine generator;
  std::uniform_int_distribution<int> distribution(MinGen, MaxGen);
  

  const int nrolls = 10000; // number of experiments
  
  // generate numbers, make a histogram of distribution
  std::map<int, int> histogram;
  for (int i = 0; i < nrolls; i++) {
  	
    int random_number = distribution(generator); // here's where # is generated
    random_number = keep_out_of_range(10, 20, random_number);
    
    ++histogram[random_number];
  }

  // print the histogram
  for (auto p : histogram) {
    std::cout << std::fixed << std::setprecision(1) << std::setw(2)
              << p.first << ' ' << std::string(p.second/(nrolls/(10*(MaxGen-MinGen))), '*') << '\n';
  }
  return 0;
}


Not that it still isn't uniform, because they way we "pushed" numbers in the [10, 20] range out will make numbers surrounding the [10, 20] range more likely to be hit.

-6 ********
-5 ***********
-4 **********
-3 **********
-2 ************
-1 **********
0 *********
1 **********
2 ************
3 **************
4 **********************
5 ***********************
6 ***************
7 *****************
8 *******************
9 ***********************
21 **********************
22 ******************
23 ********************
24 **********************
25 *****************
26 **********
27 ********
28 *********
29 **********
30 ***********
31 ***********
32 *********
33 ********
34 *********


Note: You could have unbounded + deterministic runtime as well, change uniform_int_distribution to normal_distribution.

_______________________________________________

Anyway.... all of this is complicated, and it's begging the question: What do you want to do with your random numbers after you generate them?

Usually, for things such as games, people want a bounded output, usually with a uniform distribution. Ex: Bounding between 0 and 100, or bounding between the coordinates of the player map.

My first post has a generating method that is unbounded, meaning that there's a chance of the number 1 million being generated. If, in your program, you generate 1 million, what are you planning on doing with it?
Last edited on
One way to do this without bias is to borrow a technique from stochastic rounding:
https://en.wikipedia.org/wiki/Rounding#Stochastic_rounding
The idea is that you would select between the two sides of the distribution randomly.

For e.g., a standard normal distribution, we would fold it, yielding the half-normal distribution
https://en.wikipedia.org/wiki/Half-normal_distribution , and then randomly select the side of the excluded range on which the random variable falls with the appropriate probability. If the left side of the excluded range is selected, the random variable (which is always positive) would be subtracted from the lower bound (10). Otherwise, the random variable would be added to the upper bound (20).
Last edited on
Y'all are overthinking it. Non-biased, any range, normal-distribution:

1
2
3
4
5
6
7
8
type get_me_a_special_random_number()
{
  while (true)
  {
    type n = get_me_a_random_number();
    if (n < 10 or 20 < n) return n;
  }
}

Simply ignoring invalid values is _the_ way to get a random value without introducing bias.
Right, but that takes unbounded time (Edit: It potentially takes a long time). This is most likely not a problem for OP, but it may be if most of the generated values fall in the bounds [10, 20]. For example, perhaps get_me_a_random_number() returns uniformly distributed integers in [9, 21].


Last edited on
No, it doesn't take unbounded time, and the objection is contrived.

Granted, if you are looking for {0, 1} in [0,264], you might be sitting there for a while. In that case it might be wise to rethink your sampling method.

There is no magic answers-everything way to do things when dealing with random distributions. Use your brain.

Remember, though, that sampling without discard always introduces bias. You can't just chop a distribution at one end without getting hit by a bunch of pigeons.
No, it doesn't take unbounded time

Yes, my mistake. Eventually an acceptable value will be sampled. As you note, with bad luck or a stupidly low success parameter, this might take a while.

However, I disagree that my objection was contrived.
Already 90% of generated values would need to be discarded, so you're looking at an algorithm which is quite likely to take significantly (some factor) longer. It is not an extreme example.
Last edited on
I thought a recursion based function might work but though the following program gives correct results often it crashes - I suspect this is when the first drawn random number is in the zone of exclusion. Any idea why?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# include <random>
# include <iostream>
# include <chrono>

auto truncRand(const int lowerNum, const int upperNum)
{
    auto seed = std::chrono::system_clock::now().time_since_epoch().count();//seed
    std::default_random_engine dre(seed);//engine
    std::uniform_int_distribution<int> di(-100, 100);//
    auto num = di(dre);
    if (num < lowerNum || num > upperNum)
    {
        return num;
    }
    else
    {
        truncRand(lowerNum, upperNum);
    }

}

int main()
{
        std::cout << truncRand(10, 20);
}

ps: you can narrow the range of the uniform int distribution to make the crash more likely
Last edited on
> often it crashes - Any idea why?

Compile with warnings enabled and you would be enlightened.


There is no need to generate more than one random number:
1
2
3
4
5
6
7
8
9
10
11
#include <random>

// invariant: minv < less_than <= greater_than < maxv
int excluded_random( int less_than, int greater_than, int minv, int maxv )
{
    static std::mt19937 rng( std::random_device{}() ) ;

    const int span = greater_than - less_than + 1 ;
    const int r = std::uniform_int_distribution<int>( minv, maxv-span )(rng) ;
    return r < less_than ? r : r+span ;
}
Last edited on
Y'all are overthinking it. Non-biased, any range, normal-distribution:

But what should you set as the variance and mean of your normal distribution?

No, it doesn't take unbounded time,
Can you please explain why it doesn't? You theoretically could keep getting 17 or 15. By "unbounded" I mean you can't assign a complexity to it, O(something), right? You could assign an average time complexity (if the distribution has an average, post-exclusion values). But I agree the threat of this is astronomically low as long as you plan it correctly.

"Not only does this fix the distribution problem, it also avoids the low order bit problem entirely. The down side to this solution is that the performance is dictated by the range. A small range could potentially call rand many times. Unfortunately, there is not a simple workaround for this if..." http://www.eternallyconfuzzled.com/arts/jsw_art_rand.aspx

Remember, though, that sampling without discard always introduces bias. You can't just chop a distribution at one end without getting hit by a bunch of pigeons.
Absolutely, it makes no sense to have a uniform distribution between -infinity and infinity.

What I was trying to get at was the fact that, usually, random numbers are a means to an end. OP wanted to generate numbers seemingly in the range (-infinity, 0) ∪ (10, infinity). I was trying to ask him what he would do when the RNG spat out 5,872,125. Does that number actually mean anything in OP's simulation? If it doesn't, then OP probably doesn't want an unbounded output. The variance I chose was very arbitrary, I had no idea how big or small OP actually wanted his numbers to be in, say, +/-1 standard deviation.

If I had a game map, and I wanted enemies to spawn with a normal distribution around the center, I would want to make sure there's at least a 95% chance of the enemy spawning within the bounds of the map, or else the discarded values could possibly become a bottleneck. Analyzing/debugging stochastic behavior is hard, and is best to avoid unless necessary, in my opinion.
Last edited on
What I wanted was a random position that's actually off the screen. So I would take whatever the random number was and say %1000 for screen size x=1000.
Interesting, so would the end result number still be on the screen, between coordinates 0 to 999, inclusive?

Edit: Okay I think I see what you mean, you want random values outside of your screen box [0, 999]. But how far do you want those values to go out? To infinity, or only so, say, 2000 or -1000?

ex: [-1000 ..... -1 excluded] [0 .... 999 excluded] [1000 ..... 2000 included]

If both the left-hand-side of the excluded bound and the right-hand-side of the excluded bound are finite, you can do this easily with uniform_int_distribution or rand()%[some bounds].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Example program
#include <iostream>
#include <string>
#include <cstdlib>

int generate_random_number_offscreen(int screen_size)
{
    int r = rand() % screen_size;
    if (rand()%2 == 0) // pick between left of screen, or right of screen
        r -= screen_size; // range[-1000, -1] inclusive
    else
        r += screen_size; // range[1000, 1999] inclusive
    return r;
}

int main()
{
    const int screen_size = 1000;
    
    for (int i = 0; i < 10; i++)
    {
        std::cout << generate_random_number_offscreen(screen_size) << std::endl;
    }
}


This makes the random number be between in range
[-screen_size, -1] ∪ [screen_size, 2 * screen_size - 1]

___________________________________________________

EDIT:
Here's the C++11 <random> way of doing it

https://ideone.com/EN0G7W

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Example program
#include <iostream>
#include <random>


int generate_random_number_offscreen(int screen_size)
{
    static std::default_random_engine gen;
    static std::uniform_int_distribution<int> dist(0, screen_size-1);
  
    int r = dist(gen);
    if (dist(gen)%2 == 0) // pick between left of screen, or right of screen
        r -= screen_size; // range[-1000, -1] inclusive
    else
        r += screen_size; // range[1000, 1999] inclusive

    return r;
}

int main()
{
    const int screen_size = 1000;
    
    for (int i = 0; i < 10; i++)
    {
        std::cout << generate_random_number_offscreen(screen_size) << std::endl;
    }
}


Hope that helps :p
Last edited on
way over-thinking it.
val = pow(-1, rand())* rand(); //makes a random number from -randmax to randmax
if(val > 0) val += 10; //if its positive, push it to be > 10, and if it overflows, its negative again.

there is probably a simple way to express that in the c++11 random tools, but I am not there yet.


%1000 is 0-999. Presumably, 0,0 is on the screen (but is it a corner or the center?). So I would think you need to generate bigger than 1000 and possible less than -1000 range.
which is exactly the original question..

pow(-1, rand()) * (rand() %something + 1000)) where 'something' prevents the overflow/wraparound problem.
Last edited on
if it overflows, its negative again
Please no, that's undefined behavior.

But yes that would work. I had actually just edited my post with something similar, it uses rand()%2 instead of pow(-1, rand()).

Edit: Edited my previous post to show C++11 <random> way of doing it.

________________

Edit 2: @jonnin

pow(-1, rand()) * (rand() %1000+ 1000))
Not a big deal, but it has slight problem in that would make values -1000 to -1 impossible. The values produced would still correctly be offscren, but it's biased toward a larger negative average away from the center of the screen. Using pow() instead of an if statement is clever though.
Last edited on
Pages: 12