Go中的shuffle数组

dea*_*mon 74 arrays go

我试图将以下Python代码翻译为Go

import random

list = [i for i in range(1, 25)]
random.shuffle(list)
print(list)
Run Code Online (Sandbox Code Playgroud)

但发现我的Go版本冗长而笨拙,因为没有shuffle函数,我必须实现接口和转换类型.

什么是我的代码的惯用Go版本?

Eva*_*haw 99

dystroy的答案是完全合理的,但也可以在不分配任何额外切片的情况下进行洗牌.

for i := range slice {
    j := rand.Intn(i + 1)
    slice[i], slice[j] = slice[j], slice[i]
}
Run Code Online (Sandbox Code Playgroud)

有关算法的更多详细信息,请参阅此Wikipedia文章.rand.Perm实际上也在内部使用这个算法.

  • 我认为这是文章中的"由内而外"版本,你忽略了`i!= j`检查? (4认同)

Den*_*ret 90

由于您的列表只是1到25之间的整数,您可以使用Perm:

list := rand.Perm(25)
for i, _ := range list {
    list[i]++
}
Run Code Online (Sandbox Code Playgroud)

请注意,使用给定的排列rand.Perm是一种有效的方式来混洗任何数组.

dest := make([]int, len(src))
perm := rand.Perm(len(src))
for i, v := range perm {
    dest[v] = src[i]
}
Run Code Online (Sandbox Code Playgroud)


Von*_*onC 32

Go 1.10可能包括官方的Fisher-Yates shuffle函数.

CL 51891:

math/rand:添加Shuffle

Shuffle使用Fisher-Yates算法.

由于这是新的API,它使我们有机会使用更快的Int31n实现,这主要避免了划分.

因此,尽管需要单独的初始化循环并使用函数调用交换元素,但BenchmarkPerm30ViaShuffle速度比快30%BenchmarkPerm30.

文档: pkg/math/rand/#Shuffle

例:

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)
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记随机播种,否则您将始终得到相同的顺序。例如“rand.Seed(time.Now().UnixNano())”。 (3认同)

小智 7

回答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]
    }
Run Code Online (Sandbox Code Playgroud)

遵循维基百科文章中的相同算法.