【问题标题】:Pick a random value from a Go Slice从 Go Slice 中选择一个随机值
【发布时间】:2016-03-03 20:34:21
【问题描述】:

情况:

我有一个值,需要从中随机选择一个值。然后我想将它与一个固定的字符串连接起来。到目前为止,这是我的代码:

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 )
}

问题:

是否有内置函数,可以通过“选择随机原因”部分来帮助我?

【问题讨论】:

  • 相关,您可能会发现这个问题和答案很有用:How to generate a random string of a fixed length in golang? 它包含您的问题作为子任务,即从一片符文或字符中选择随机字母。它可能会为您提供一些关于 Go 的“随机”世界、背景发生了什么以及不同解决方案的效率的好建议。
  • 您的问题的答案在 math/rand 包文档中:golang.org/pkg/math/rand - 检查第一个代码示例;)
  • 提示:gofmt 你的代码

标签: random go slice


【解决方案1】:

使用 rand 包中的函数 Intn 来选择随机索引。

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))])

其他解决方案是使用Rand 对象。

s := rand.NewSource(time.Now().Unix())
r := rand.New(s) // initialize local pseudorandom generator 
r.Intn(len(reasons))

【讨论】:

  • 为了记录,rand.Seed... 也是需要的。
【解决方案2】:

只需选择一个随机整数模切片长度:

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])

游乐场:http://play.golang.org/p/fEHElLJrEZ。 (注意对rand.Seed的赞扬。)

【讨论】:

  • 这不如rand.Intn(len(reasons))。只需使用 % 在 [0, len(reasons)) 的每个值上的概率不相等。
  • 真的!模块化偏差!
【解决方案3】:

由于这仍然出现在 Google 的 Golang 随机字符串生成的顶级结果中,我想分享我一直在使用的东西。

这是我正在使用的解决方案:

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)
}

结果:

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

来源明智,我从 fastrand 复制了 getRandomUint32():https://github.com/valyala/fastrand

以及上面提出的解决方案。性能并没有什么不同,但我想分享结果。

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))]
}

结果:

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

这些结果只在本地运行了几次,我抓住了似乎是中值的结果。在迭代等方面肯定还有更多工作要做,但我只是想分享我所拥有的。

【讨论】:

  • “这个[调用Seed]正在采取......自行执行”。您只需 一次 为随机数生成器播种。作为一次性启动电话,它所花费的时间应该是无关紧要的。
  • 什么意思?如果您没有在每个 randomOpt() 调用中播种随机数,那么您将从 randomOpt() 获得相同的结果。该号码需要在每次通话时随机生成。我的解决方案的结果:{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
  • 另一个解决方案:{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 是 func randomOpt() string { rand.Seed(start.Unix()) return opts[rand.Intn(len(opts))] }
  • “如果您没有在每个 randomOpt() 调用中播种随机数” ... 这不是播种的工作方式。您为随机数生成器播种一次,然后重复调用同一个生成器以获取不同的值。
  • 啊,明白了。将种子放在 Main() 中并且不对每个请求调用种子,我将其标记为缓慢的原因是不正确的。修复种子,该解决方案仍然比我提供的解决方案慢:{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
猜你喜欢
  • 2012-02-21
  • 2013-10-20
  • 2019-07-30
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
  • 2018-07-23
  • 2021-01-11
  • 2014-06-19
相关资源
最近更新 更多