68

Situation:

I've a slice of values and need to pick up a randomly chosen value from it. Then I want to concatenate it with a fixed string. This is my code so far:

func main() {
//create the reasons slice and append reasons to it
reasons := make([]string, 0)
reasons = append(reasons,
    "Locked out",
    "Pipes broke",
    "Food poisoning",
    "Not feeling well")

message := fmt.Sprint("Gonna work from home...", pick a random reason )
}

Question:

Is there a built-in function, which can help me by doing the "pick a random reason" part?

Rene Knop
  • 1,686
  • 3
  • 13
  • 25
Amistad
  • 6,540
  • 11
  • 43
  • 71
  • 1
    Related, you might find this question and answer useful: [How to generate a random string of a fixed length in golang?](http://stackoverflow.com/a/31832326/1705598) It contains your question as a sub-task, that is, choosing random letters from a slice of runes or characters. It might give you some good tips about the "random" world of Go, what's going on in the background and efficiency of different solutions. – icza Nov 30 '15 at 09:14
  • 3
    The answer to your question is in math/rand package documentation: https://golang.org/pkg/math/rand/ - check the first code example ;) – kostya Nov 30 '15 at 10:20
  • 2
    Tips:gofmt your code – holys Nov 30 '15 at 10:45

3 Answers3

124

Use function Intn from rand package to select a random index.

import (
  "math/rand"
  "time"
)

// ...

rand.Seed(time.Now().Unix()) // initialize global pseudo random generator
message := fmt.Sprint("Gonna work from home...", reasons[rand.Intn(len(reasons))])

Other solution is to use Rand object.

s := rand.NewSource(time.Now().Unix())
r := rand.New(s) // initialize local pseudorandom generator 
r.Intn(len(reasons))
jdlm
  • 5,822
  • 4
  • 28
  • 47
Grzegorz Żur
  • 44,524
  • 14
  • 110
  • 102
22

Just pick a random integer mod slice length:

rand.Seed(time.Now().Unix())
reasons := []string{
    "Locked out",
    "Pipes broke",
    "Food poisoning",
    "Not feeling well",
}
n := rand.Int() % len(reasons)
fmt.Print("Gonna work from home...", reasons[n])

Playground: http://play.golang.org/p/fEHElLJrEZ. (Note the commend about rand.Seed.)

Ainar-G
  • 31,424
  • 10
  • 89
  • 112
-2

Since this still shows up in Google's top results for Golang random string generation, I wanted to share what I have been working with.

Here is the solution I am using:

package main

import (
  "fmt"
  "strings"
  "time"
)

var (
  opts  = strings.Split("option1,option2,option3", ",")
  start = time.Now()
)

func main() {
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
}

func getRandomOpt() string {
  len := len(opts)
  n := uint32(0)
  if len > 0 {
    n = getRandomUint32() % uint32(len)
  }
  return opts[n]
}

func getRandomUint32() uint32 {
  x := time.Now().UnixNano()
  return uint32((x >> 32) ^ x)
}

And results:

option2 665ns
option1 41.406µs
option1 44.817µs
option3 47.329µs
option1 49.725µs
option3 52µs
option2 54.393µs
option2 56.798µs
option1 59.098µs

Source wise, I copied getRandomUint32() from fastrand: https://github.com/valyala/fastrand

And the solution proposed above. Performance isn't all that different, but I wanted to share results.

package main

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

var (
  opts  = strings.Split("option1,option2,option3", ",")
  start = time.Now()
)

func main() {
  rand.Seed(start.Unix())
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
  fmt.Println(getRandomOpt(), time.Since(start))
}

func getRandomOpt() string {
  return opts[rand.Intn(len(opts))]
}

And results:

option3 11.865µs
option2 48.415µs
option3 52.809µs
option1 55.536µs
option3 58.191µs
option3 60.793µs
option1 63.391µs
option2 65.982µs
option2 68.601µs

These results were only run a few times locally and I grabbed what appeared to be the median result. There is certainly more work to be done in terms of iterations and whatnot, but I just wanted to share what I have.

  • "This [calling `Seed`] was taking … to execute on its own". You only seed a random number generator **once**. As a one time start-up call the time it takes should be irrelevant. – Dave C Aug 28 '19 at 11:29
  • What do you mean? If you're not seeding a random number on each randomOpt() call, you're going to get the same result from randomOpt(). The number needs to be randomly generated on each call. My solution's results: ```{option3} 697ns {option2} 50.316µs {option2} 54.371µs {option3} 57.262µs {option2} 60.501µs {option3} 63.108µs {option3} 66.74µs {option1} 69.45µs {option1} 73.442µs``` – Tyler Tyssedal Aug 28 '19 at 14:44
  • The other solution: ```{option1} 9.982µs {option1} 62.222µs {option1} 75µs {option1} 86.793µs {option1} 98.493µs {option1} 110.601µs {option1} 122.225µs {option1} 133.76µs {option1} 145.352µs``` which was ```func randomOpt() string { rand.Seed(start.Unix()) return opts[rand.Intn(len(opts))] } ``` – Tyler Tyssedal Aug 28 '19 at 14:45
  • "If you're not seeding a random number on each randomOpt() call" ... That's not how seeding works. You seed a random number generator **once** and then you call that same generator repeatedly getting different values. – Dave C Aug 28 '19 at 15:03
  • Ah got it. Putting the seed inside the Main() and not calling seed on each request was incorrect of me to label as the cause of the slowness. Fixing the seed, that solution is still slower than the one I provided: ```{option2} 10.01µs {option1} 68.63µs {option3} 72.933µs {option3} 75.834µs {option1} 97.274µs {option3} 100.264µs {option2} 102.983µs {option3} 105.97µs {option2} 108.537µs``` – Tyler Tyssedal Aug 28 '19 at 15:57
  • I am going to update my post to reflect this, offering both full options. After start-up, the performance isn't all that different, but I want my post to reflect it accurately. – Tyler Tyssedal Aug 28 '19 at 16:10