93

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

import random

list = [i for i in range(1, 25)]
random.shuffle(list)
print(list)

但发现我的 Go 版本冗长且笨拙,因为没有 shuffle 功能,我必须实现接口和转换类型。

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

4

8 回答 8

108

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

for i := range slice {
    j := rand.Intn(i + 1)
    slice[i], slice[j] = slice[j], slice[i]
}

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

于 2012-09-04T16:12:38.940 回答
99

由于您的列表只是从 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]
}
于 2012-09-04T13:48:36.120 回答
59

从 1.10 开始,Go 包含了官方的Fisher-Yates shuffle函数。

文档:pkg/math/rand/#Shuffle

数学/兰德:添加随机播放

Shuffle 使用 Fisher-Yates 算法。

由于这是新的 API,它为我们提供了使用更快的Int31n实现的机会,该实现主要避免了分裂。

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

另见原始CL 51891

首先,正如shellll所评论的:

不要忘记随机播种,否则您将始终获得相同的订单。
例如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)
于 2017-09-12T21:42:13.617 回答
9

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

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

于 2017-03-14T02:20:20.933 回答
2

也许您还可以使用以下功能:

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]
   }
}
于 2018-05-03T18:46:22.617 回答
2

使用库中的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库,因此需要播种。有关更多详细信息,请参见此处

于 2019-05-30T18:17:42.133 回答
1

使用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

于 2017-06-06T02:16:34.003 回答
1

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]

而且,不要忘记一点点复制比一点点依赖更好

于 2017-07-24T06:35:39.163 回答