views:

139

answers:

5

Many random-number generators return floating numbers between 0 and 1.

What's the best and correct way to get integers between a and b?

A: 
X = (Rand() * (B - A)) + A
Yossarian
I'm sorry, I forgot to add that I'm interested in integers.
Georg
And where is problem? if `Rand` is your function returning float, you can use this to convert <0;1> interval into <A;B> interval, and then round it using `Math.Floor`, cast to `int`, ...
Yossarian
A: 

Assuming r_a_b is the desired random number between a and b and r_0_1 is a random number between 0 and 1 the following should work just fine:

r_a_b = (r_0_1 * (b-a)) + a
Joachim Sauer
I'm sorry, I forgot to add that I'm interested in integers.
Georg
@Ralph: Georg added the information that he needs an integer result after I posted this answer and for a floating point result my code is correct.
Joachim Sauer
+1  A: 

Another way to look at it, where r is your random number in the range 0 to 1:

(1-r)a + rb

As for your additional requirement of the result being an integer, maybe (apart from using built in casting) the modulus operator can help you out. Check out this question and the answer:

http://stackoverflow.com/questions/137783/given-a-function-which-produces-a-random-integer-in-the-range-1-to-5-write-a-fun

zaf
I don't think the linked question is of use here, since in that question the generated random number is an integer value.
Joachim Sauer
In the question, whats that word in bold?
zaf
@zaf: I think Joachim Sauer is referring to the input, not the output. In the linked question, a function that returns a random integer between 1 and 5 is transformed into a function that returns an integer between 1 and 7. Those problems are not equivalent.
Tim Pietzcker
@Tim - Yes. I probably shouldn't have linked that question but I'll leave it there. It may give someone an idea.
zaf
A: 

Well, why not just look at how Python does it itself? Read random.py in your installation's lib directory.

After gutting it to only support the behavior of random.randint() (which is what you want) and removing all error checks for non-integer or out-of-bounds arguments, you get:

import random
def randint(start, stop):
    width = stop+1 - start
    return start + int(random.random()*width)

Testing:

>>> l = []
>>> for i in range(2000000):
...     l.append(randint(3,6))
...
>>> l.count(3)
499593
>>> l.count(4)
499359
>>> l.count(5)
501432
>>> l.count(6)
499616
>>>
Tim Pietzcker
+4  A: 

Divide the interval [0,1] in B-A+1 bins

Example A=2, B=5

        [----+----+----+----]
        0    1/4  1/2  3/4  1
Maps to    2    3    4    5

The problem with the formula

 Int (Rnd() * (B-A+1)) + A

is that your Rnd() generation interval is closed on both sides, thus the 0 and the 1 are both possible outputs and the formula gives 6 when the Rnd() is exactly 1.

In a real random distribution (not pseudo), the 1 has probability zero. I think it is safe enough to program something like:

 r=Rnd()
 if r equal 1
     MyInt = B
 else
     MyInt = Int(r * (B-A+1)) + A
 endif
belisarius
Or if you prefer, mapping the 6 to 2 instead of to 5 // mod(Int(r*(B-A+1), (B-A+1)))+A
belisarius
In Python, `random.random()` returns a value between 0 and "just below" 1, so also the pseudo-random number is safe. See http://stackoverflow.com/questions/3037952
Tim Pietzcker
@Tim Pietzcker That's good! (although implementation dependent)
belisarius
@user359996 The question is tagged as "language-agnostic", so I tried to give a general answer. I believe the algorithm works irrespective to the openness of the source interval. The usual notation in mathematics is to use "[..]" for closed intervals and "(..)" for open ones. And the OP uses "[..]"
belisarius