Consistently Random Selection in Go
I am relatively new to Go and have found it relatively easy to pick up how to do things, but every now and then something trips me up.
A simple task I gave myself was to rewrite a script I wrote in Ruby, in Go. This script takes a list of my coworkers and picks 3 of them at random to alert about my new pull request. This is a task that I have to do multiple times throughout the work day, which is why a script is ideal.
# Ruby
possible_reviewers = %w(
afogel
ambriz
brett
brian
christian
jared
jason
megan
Patrick
joe
stan
aaron
)
three_reviewers = possible_reviewers.sample(3).join(‘, ‘)
print “#{three_reviewers}\n#{note}\n”
I begin with a Ruby Array and use it’s the standard library :sample
to select 3 people. I rewrote this in Go rather easily, see below.
# Go
import (
“math/rand”
“fmt”
)
var possible = []string{
“afogel”,
“ambriz”,
“brett”,
“brian”,
“christian”,
“jared”,
“jason”,
“megan”,
“Patrick”,
“joe”,
“stan”,
“aaron”,
}
randomize := rand.Perm(len(possible))
for _, v := range randomize[:3] {
fmt.Println(possible[v])
}
This is pretty straight forward, I start with an Array in Go and randomize using the Perm() function from the math/rand
library. The function takes and integer as an argument but returns a pseudo-random permutation of the integers [0,n)
from the default Source.
The only issue with randomizing in Go is the math/rand
is deterministic by default, which means the randomization only happens once and returns the same random numbers each time.
Don’t believe me? See for yourself in the playground:
see this code in play.golang.org
rand.Perm(10) // returns [9 4 2 6 8 0 3 1 7 5] everytime
I spent sometime trying to figure this out and found two very imformative threads with solutions for my problem.
math/rand: Deterministic random by default is dangerous · Issue #11871 · golang/go
Random generator always returns same value on every run
The easiest and most legit solution is seeding rand
with something. By default the seed is 1
which is why the same result is return, but by seeding my rand
with time.Now().UnixNano()
, I get something more random, which is expected.
rand.Seed(time.Now().UnixNano())
I am basically just getting a current time stamp to ensure each run of the program will be a different result.
package main
import "fmt"
import "math/rand"
import "time"
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Perm(10))
}