web-dev-qa-db-ja.com

Goの配列をシャッフルする

私は次のPythonコードをGoに翻訳しようとしました

import random

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

シャッフル機能がなく、インターフェイスを実装して型を変換しなければならなかったため、Goバージョンが長くて扱いにくいと感じました。

コードの慣用的なGoバージョンとは何でしょうか?

72
deamon

リストは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]
}
89
Denys Séguret

dystroyの答え は完全に合理的ですが、追加のスライスを割り当てずにシャッフルすることも可能です。

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

アルゴリズムの詳細については、 このウィキペディアの記事 を参照してください。 Rand.Permは実際にこのアルゴリズムも内部的に使用します。

99
Evan Shaw

Go 1.10には、公式の Fisher-Yates shuffle 関数が含まれている場合があります。

CL 51891 を参照してください:

math/Rand:シャッフルを追加

シャッフルはFisher-Yatesアルゴリズムを使用します。

これは新しいAPIであるため、はるかに高速なInt31n実装を使用して、ほとんど分割を回避する機会を与えてくれます。

結果として、BenchmarkPerm30ViaShuffleBenchmarkPerm30よりも約30%高速です。ただし、個別の初期化ループが必要で、関数呼び出しを使用して要素をスワップします。

ドキュメント: 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)
29
VonC

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

ウィキペディアの記事の同じアルゴリズムに従ってください。

7
heranglo

次の機能を使用することもできます。

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

レイドのアプローチ は、[]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]

また、 少しのコピーは少しの依存関係よりも優れている であることも忘れないでください

1
Joseph Buchma

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 Shomali

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 を参照してください。

0
stwykd