在Go中随机播放数组


82

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

import random

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

但是我发现Go版本冗长而笨拙,因为没有shuffle函数,我不得不实现接口和转换类型。

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


2
这个问题有一个shuffle()实现:Go中的数组处理
Sjoerd 2012年

Answers:


96

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

我不确定自此答案以来Perm方法是否已更改,但是它返回“整数[0,n)的伪随机排列”。在这种情况下,结果将是0到24的排列
。– JayJay

1
@JayJay这就是为什么数字增加的原因(另一种解决方法是将0更改为25)。
DenysSéguret18年

1
继续向下滚动,现在已在1.10版本中提供支持:stackoverflow.com/a/46185753/474189
Duncan Jones,

101

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

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

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


4
我认为这是文章中的“由内而外”的版本,您取消了i!=j支票吗?
马特·乔纳

查看Wikipedia页面,这似乎是“现代算法”(第一个变体)。“由内而外”的版本似乎将结果存储在一个新的数组中,而不是进行随机播放。
jochen

46

从1.10开始,Go包含了正式的Fisher-Yates随机播放功能。

说明文件: pkg/math/rand/#Shuffle

数学/ rand:添加随机播放

随机播放使用Fisher-Yates算法。

由于这是新的API,因此它为我们提供了使用更快的Int31n实现方式(大部分避免了划分)的机会。

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

另请参阅原始的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)

@Deleplace谢谢。我已经在答案中包含了此链接。
VonC

3
不要忘记为随机种子,否则您将始终获得相同的顺序。例如rand.Seed(time.Now().UnixNano())
Shelll

@shelll谢谢。我已将您的评论包含在答案中,以提高知名度。
VonC

7

埃文·肖的回答有一个小错误。根据同一篇文章,如果我们从最低索引到最高索引对切片进行迭代,以获得均匀(伪)的随机混洗,则必须从interval中选择一个随机整数,[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]
    }

遵循Wikipedia文章中的相同算法。


如果一个答案有错误,请编辑错误的答案,而不要编写另一个答案。
雅各布·大理石

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

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

由于[]interface{}输入,Raed的方法非常不灵活。这是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]

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


1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.