我试图将以下 Python 代码翻译成 Go
import random
list = [i for i in range(1, 25)]
random.shuffle(list)
print(list)
但发现我的 Go 版本冗长且笨拙,因为没有 shuffle 功能,我必须实现接口和转换类型。
我的代码的惯用 Go 版本是什么?
dystroy 的回答是完全合理的,但也可以在不分配任何额外切片的情况下进行洗牌。
for i := range slice {
j := rand.Intn(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
有关该算法的更多详细信息,请参阅此 Wikipedia 文章。rand.Perm
实际上在内部也使用了这个算法。
由于您的列表只是从 1 到 25 的整数,您可以使用Perm:
list := rand.Perm(25)
for i, _ := range list {
list[i]++
}
请注意,使用 给出的排列rand.Perm
是对任何数组进行洗牌的有效方法。
dest := make([]int, len(src))
perm := rand.Perm(len(src))
for i, v := range perm {
dest[v] = src[i]
}
从 1.10 开始,Go 包含了官方的Fisher-Yates shuffle函数。
数学/兰德:添加随机播放
Shuffle 使用 Fisher-Yates 算法。
由于这是新的 API,它为我们提供了使用更快的
Int31n
实现的机会,该实现主要避免了分裂。因此,尽管需要单独的初始化循环并使用函数调用来交换元素,但它
BenchmarkPerm30ViaShuffle
比 快约 30% 。BenchmarkPerm30
另见原始CL 51891
不要忘记随机播种,否则您将始终获得相同的订单。
例如rand.Seed(time.Now().UnixNano())
例子:
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {
words[i], words[j] = words[j], words[i]
})
fmt.Println(words)
Evan Shaw 的回答有一个小错误。如果我们从最低索引到最高索引遍历切片,以获得均匀(伪)随机洗牌,根据同一篇文章,我们必须从区间[i,n)
中选择一个随机整数,而不是[0,n+1)
。
该实现将满足您对较大输入的需求,但对于较小的切片,它将执行非均匀洗牌。
要利用rand.Intn()
,我们可以这样做:
for i := len(slice) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
遵循维基百科文章中的相同算法。
也许您还可以使用以下功能:
func main() {
slice := []int{10, 12, 14, 16, 18, 20}
Shuffle(slice)
fmt.Println(slice)
}
func Shuffle(slice []int) {
r := rand.New(rand.NewSource(time.Now().Unix()))
for n := len(slice); n > 0; n-- {
randIndex := r.Intn(n)
slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1]
}
}
使用库中的Shuffle()math/rand
。
这是一个例子:
package main
import (
"fmt"
"math/rand"
"strings"
)
func main() {
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {
words[i], words[j] = words[j], words[i]
})
fmt.Println(words)
}
由于它来自math/rand
库,因此需要播种。有关更多详细信息,请参见此处。
使用math/rand
包时,别忘了设置源
// Random numbers are generated by a Source. Top-level functions, such as
// Float64 and Int, use a default shared Source that produces a deterministic
// sequence of values each time a program is run. Use the Seed function to
// initialize the default Source if different behavior is required for each run.
所以我写了一个Shuffle
考虑到这一点的函数:
import (
"math/rand"
)
func Shuffle(array []interface{}, source rand.Source) {
random := rand.New(source)
for i := len(array) - 1; i > 0; i-- {
j := random.Intn(i + 1)
array[i], array[j] = array[j], array[i]
}
}
并使用它:
source := rand.NewSource(time.Now().UnixNano())
array := []interface{}{"a", "b", "c"}
Shuffle(array, source) // [c b a]
如果你想使用它,你可以在这里找到它https://github.com/shomali11/util
Raed 的方法[]interface{}
因为作为输入而非常不灵活。这是go>=1.8更方便的版本:
func Shuffle(slice interface{}) {
rv := reflect.ValueOf(slice)
swap := reflect.Swapper(slice)
length := rv.Len()
for i := length - 1; i > 0; i-- {
j := rand.Intn(i + 1)
swap(i, j)
}
}
示例用法:
rand.Seed(time.Now().UnixNano()) // do it once during app initialization
s := []int{1, 2, 3, 4, 5}
Shuffle(s)
fmt.Println(s) // Example output: [4 3 2 1 5]
而且,不要忘记一点点复制比一点点依赖更好