RubyGuides
Share this post!

Random Numbers & Strings in Ruby

In this article you’re going to learn how to get random numbers using Ruby.

Randomness can make things more interesting in games or it can help make your sites more secure. In Ruby, there are multiple ways to generate random numbers with various properties.

Let’s generate some random numbers using the rand method:

ruby random

Rand produces decimal numbers if called without arguments, but most of the time you will want whole numbers.

You can pass an argument to rand to tell it to generate a number starting from zero up to (but not including) our number.

rand 100
> 41

Get Random Number in Range

Ruby random number generation is really easy, but what if you want the number to be in a specific range instead of starting from zero?

Not a problem, you can just use a range to get exactly what you need.

Example:

rand(200..500)
> 352

How To Randomize An Array

Next, we may want to do a random pick from a list. You might be tempted to do this:

[5, 15, 30, 60].shuffle.first
> 30

But Ruby has the sample method which is better suited (and faster) for this task:

[5, 15, 30, 60].sample
> 5

We can also use sample for ranges, this code generates a random letter:

('a'..'z').to_a.sample
> b

Ruby Random Strings

The ultimate randomness application is to generate a random string with a custom character set.

Here is the code:

CHARSET = Array('A'..'Z') + Array('a'..'z')

def generate_code(number)
  Array.new(number) { CHARSET.sample }.join
end

puts generate_code(20)

There are a few things going on here.

First, we prepare our charset using ranges and converting them to arrays. Then we take advantage of calling Array.new with a block, which lets us initialize an array of size n with the values produced by the block.

This code will produce strings of the following form:

TufwGfXZskHlPcYrLNKg.

You can tweak the character set to fit your needs.

How to Seed The PRNG

Ruby uses a PRNG (Pseudo-Random Number Generator) to give you these random numbers.

This works from a starting value that we call “the seed”.

Normally you don’t have to worry about this, because the seed is generated for you automatically…

…but you can set your own seed, which means you’ll always get the same sequence of numbers.

This could be useful for testing.

Here’s how to use the srand method to change the seed:

Kernel.srand(123)

Secure Ruby Random Numbers

The numbers produced by rand might be enough for a simple application, but if you want to use them for security purposes —like generating a password reset token— then you should use SecureRandom, which is part of the Ruby standard library.

SecureRandom seeds its generator from /dev/urandom on Unix systems and on windows it uses the CryptGenRandom API.

require 'securerandom'

SecureRandom.random_number
> 0.232

As you can see this works a lot like rand, you can also pass in a max number.

SecureRandom.random_number(100)
> 72

In addition, SecureRandom has other output formats available.

Using hex you can generate a hexadecimal fixed-width string:

SecureRandom.hex

> "87694e9e5231abca6de39c58cdfbe307"

Using base64 you can generate a Base64-encoded random string:

SecureRandom.base64

> "QeIMdpxTwWlklVGsp5/qVw=="

Conclusion

That’s it! You are now ready to start using randomness in your Ruby programs 🙂

Found this post useful? Share it with your friends & subscribe to my newsletter so you don’t miss anything new!

8 comments
Maik Schmidt says 3 years ago

Thank you very much for this interesting article! Please, do not forget that there are ways to get real random numbers. I wrote a gem (https://rubygems.org/gems/realrand) for this purpose some years ago and it provides easy access to three online sources of true random numbers. You can find its documentation on Github (https://github.com/maik/RealRand).

Best,
Maik

Serguei Cambour (@belgoros) says 3 years ago

Nice article ! Thank you very much for sharing!

Matthew Berg says 3 years ago

You can pass an argument to sample for the number of elements you want back, so you can simplify this:

Array.new(number) { charset.sample }.join

to

charset.sample(number).join

You can also splat ranges, so you could create one array instead of two; e.g.

charset = [*"a".."z", *"A".."Z"]

Assuming that method is going to be called more than once, it would make sense to define it once as a constant (or possibly class variable) outside of the routine, rather than generating it each call, so more like:

CHARSET = [*"a".."z", *"A".."Z"]
def generate_code(number); CHARSET.sample(number).join

    Jesus Castello says 3 years ago

    Thank you, I didn’t know that you can pass an argument to sample. And I agree that you should only generate the charset once if you are going to use the method multiple times, but I didn’t want to make the code more complex for the examples.

      srcv says 3 years ago

      This is not true actually. `sample` with arguments generates sample without repetitions, so it’s totally different from the original code!

        Jesus Castello says 3 years ago

        You are right! Thanks for letting us know 🙂

        For reference, this is what the documentation has to say about sample:

        The elements are chosen by using random and unique indices into the array in order to ensure that an element doesn’t repeat itself unless the array already contained duplicate elements.

        In fact, if you do this:
        charset = [*'a'..'z', *'A'..'Z'].sample(100).join.size

        You only get 52 (one for each in the character set), which might not be what you want.

    Dewayne VanHoozer (@MadBomber) says 3 years ago

    One of the many things I like about Ruby is how it seems every day I get a chance to learn something new about the language. I had never seen that splat do on a range before.

    The other thing I learned today is that coping code that has smart quotes in it gets a syntax error.

Panagiotis Atmatzidis says 3 years ago

o = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten
string = (0...50).map { o[rand(o.length)] }.join

From Stack Overflow works fine.

I used a variant in one of my scripts works awesomely well and it’s fast. That said, it’s way better to write code you and others can understand on the fly in production mode, than smart-ass long snippets copy-pasted from SO.

I think creating a method (like a black box) is the way to go.

Comments are closed