Go 中的随机排列数组

问题描述 投票:0回答:7

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

import random

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

但是发现我的 Go 版本又长又尴尬,因为没有 shuffle 功能,而且我必须实现接口和转换类型。

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

arrays go
7个回答
122
投票

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

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

请参阅这篇维基百科文章,了解有关该算法的更多详细信息。

rand.Perm
实际上也在内部使用了这个算法。


106
投票

由于您的列表只是从 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]
}

79
投票

Go 1.20(2022 年第 4 季度):正如我在“如何正确播种随机数生成器”中提到的,您不再需要

rand.Seed()
下面的答案。


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

文档:

pkg/math/rand/#Shuffle

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

Shuffle 使用 Fisher-Yates 算法。

由于这是新的 API,它为我们提供了机会 使用更快的

Int31n
实现,基本上可以避免除法。

因此,

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

另请参阅原件 CL 51891

首先,正如 shelll 评论

不要忘记随机播种,否则你将始终得到相同的顺序。
例如

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)

10
投票

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

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


4
投票

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

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

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


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]

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

© www.soinside.com 2019 - 2024. All rights reserved.