Generate a Random Int in Go

There are two libraries for generating random integers in Go, one is deterministic (and can be predicted as a result) and the other is cryptographically secure. Each serves their own purpose.

The deterministic one can be used in situations where you want random numbers, but potentially want to be able to reproduce results, this is often useful when first writing an application, testing, or debugging where you want to eliminate differences based on the random input.

If what you are after is a truly random number, that in theory no one could predict, then you’ll want to use the crypto/rand library.

So, how do you generate a random int deterministically? Easy…import math/rand, then call rand.Intn(max). This will generate a number in the range 0 to max-1.

By default the random number generator is “seeded” with the value 1, this means it is possible to predict what random numbers will be generated. If we want to make it more difficult to predict, the classic technique is to seed it using the current time. Keep in mind, if you can guess the time it was seeded, you can predict the numbers that will be generated.

package main

import (
        "fmt"
        "math/rand"
        "time"
)

func main() {
        // Uses default seed of 1, result will be 81:
        fmt.Println(rand.Intn(1000))

        // Seed the random number generator using the current time (nanoseconds since epoch):
        rand.Seed(time.Now().UnixNano())

        // Hard to predict...but is it possible? I know the day, and hour, probably minute...
        fmt.Println(rand.Intn(1000))
}

If i run it three time, the output is:

$ go run determ.go
81
465
$ go run determ.go
81
259
$ go run determ.go
81
155

As you can see, the first number is always the same, the second varies.

If we want something cryptographically secure. There is a little bit more to it, crypto/rand provides an io.Reader that can be used to generate random numbers. rand.Int() takes as arguments an io.reader and a max value that needs to be a big.Int. Generating it is still relatively straight forward:

package main

import (
        "fmt"
        "crypto/rand"
        "math/big"
)

func main() {
        // Cryptographically secure.
        n, err := rand.Int(rand.Reader, big.NewInt(1000))

        if err != nil {
                panic(err)
        }

        fmt.Println(n)
}

As always, check the package documentation for more detail:

I’ll also write something up that covers generating other number types, as well as a cryptographically secure byte slice that could be used, for example, as an encryption key.

Leave a Reply

Your email address will not be published. Required fields are marked *