Say you want to generate a random number between 1 and 1 billion:
rand(1..1_000_000_000)
Will Ruby create an array from that range every time you call this line of code?
Rubocop suggests this approach over rand(1_000_000_000)+1
but it seems there's potential for pain.
Ruby's docs say this:
# When +max+ is a Range, +rand+ returns a random number where
# range.member?(number) == true.
Where +max+
is the argument passed to rand
, but it doesn't say how it gets the number
argument. I'm also not sure if calling .member?
on a range is performant.
Any ideas?
I can use benchmark but still curious about the inner workings here.
No, Ruby will not create an array from that range, unless you explicitly call the
.to_a
method on theRange
object. In fact,rand()
doesn't work on arrays -.sample
is the method to use for returning a random element from an array.The
Range
class includesEnumerable
so you get Enumerable's iteration methods without having to convert the range into an array. The lower and upper limits for a Range are(-Float::INFINITY..Float::INFINITY)
, although that will result in aNumerical argument out of domain
error if you pass it intorand
.As for
.member?
, that method simply calls a C function calledrange_cover
that calls another one calledr_cover_p
which checks if a value is between two numbers or strings.To test the difference in speed between passing a range to
rand
and callingsample
on an array, you can perform the following test:As you can see in the first example, passing in a
range
as a parameter torand
is extremely rapid.Contrarily, calling
.to_a.sample
on a range is rather slow. This is due to the array creation process which requires allocating the appropriate data into memory. The.sample
method should be relatively fast as it simply passes a random and unique index into the array and returns that element.To check out the code for
range
have a look here.