Go中的随机数组

Posted

技术标签:

【中文标题】Go中的随机数组【英文标题】:shuffle array in Go 【发布时间】:2012-08-29 04:33:52 【问题描述】:

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

import random

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

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

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

【问题讨论】:

这个问题有一个 shuffle() 实现:Treatment of Arrays in Go. 【参考方案1】:

dystroy's answer 完全合理,但也可以在不分配任何额外切片的情况下进行随机播放。

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

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

【讨论】:

我认为这是文章中的“由内而外”版本,而您忽略了i!=j 检查? 查看***页面,这似乎是“现代算法”(第一个变体)。 “由内而外”的版本似乎将结果存储在一个新的数组中,而不是在原地进行随机播放。 警告:不,这不是“由内而外”或“现代”。我反对这个建议。 “由内而外”算法适用于数组/切片的副本,即操作两个数组/切片:sourcea(已洗牌)。在这里,我们只有一个,它声称可以就地运行。它也不是“现代的”,因为“现代”应该从切片的末尾迭代到开头(不包括第一个元素)。在这里,它从第一个元素迭代到最后(包括两者)。迭代方向或生成j 的方式都应该改变。【参考方案2】:

由于您的列表只是 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 这就是数字增加的原因(另一种解决方案是将 0 更改为 25)。 继续向下滚动,现在在 1.10 中支持开箱即用:***.com/a/46185753/474189【参考方案3】:

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

文档:pkg/math/rand/#Shuffle

math/rand:添加随机播放

Shuffle 使用 Fisher-Yates 算法。

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

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

另见原文CL 51891

首先,作为commentedshelll:

不要忘记随机播种,否则您将始终得到相同的订单。 例如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 谢谢。我已在答案中包含此链接。 不要忘记随机播种,否则您将始终得到相同的订单。例如rand.Seed(time.Now().UnixNano()). @shelll 谢谢。我已将您的评论包含在答案中以提高知名度。【参考方案4】:

Answer by Evan Shaw 有一个小错误。如果我们从最低索引到最高索引遍历切片,以获得均匀(伪)随机洗牌,根据same article,我们必须从区间[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]
    

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

【讨论】:

如果答案有错误,则编辑错误答案,而不是编写另一个答案。【参考方案5】:

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

func main() 
   slice := []int10, 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]
   

【讨论】:

【参考方案6】:

使用math/rand 库中的Shuffle()。

这是一个例子:

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 库,因此需要播种。详情请见here。

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

Raed's approach 非常不灵活,因为[]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 := []int1, 2, 3, 4, 5
    Shuffle(s)
    fmt.Println(s) // Example output: [4 3 2 1 5]

另外,别忘了a little copying is better than a little dependency

【讨论】:

以上是关于Go中的随机数组的主要内容,如果未能解决你的问题,请参考以下文章

Go数组反转练习

GO GOLANG 生成范围随机数

为什么 go 中的 map 的遍历是随机的?

Go随机数

IDL 中 如何从一个数组中随机取出3个数?

VB程序设计,用随机函数产生10个50以内(包括50)的随机整数存入一维数组。求出最大值并和数组中的首个