web-dev-qa-db-fra.com

shuffle array dans Go

J'ai essayé de traduire le code suivant Python to Go

import random

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

mais j'ai trouvé ma version Go longue et maladroite car il n'y a pas de fonction de lecture aléatoire et j'ai dû implémenter des interfaces et convertir des types.

Quelle serait une version Go idiomatique de mon code?

72
deamon

Comme votre liste n'est que les entiers de 1 à 25, vous pouvez utiliser Perm :

list := Rand.Perm(25)
for i, _ := range list {
    list[i]++
}

Notez que l'utilisation d'une permutation donnée par Rand.Perm est un moyen efficace de mélanger n'importe quel tableau.

dest := make([]int, len(src))
perm := Rand.Perm(len(src))
for i, v := range perm {
    dest[v] = src[i]
}
89
Denys Séguret

réponse de dystroy est parfaitement raisonnable, mais il est également possible de mélanger sans allouer de tranches supplémentaires.

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

Voir cet article Wikipedia pour plus de détails sur l'algorithme. Rand.Perm utilise également cet algorithme en interne.

99
Evan Shaw

Go 1.10 peut inclure une fonction officielle Fisher-Yates shuffle .

Voir CL 51891 :

math/Rand: ajouter Shuffle

Shuffle utilise l'algorithme Fisher-Yates.

Comme il s'agit d'une nouvelle API, elle nous offre la possibilité d'utiliser un _ beaucoup plus rapide Int31n implémentation qui évite surtout la division.

Par conséquent, BenchmarkPerm30ViaShuffle est environ 30% plus rapide que BenchmarkPerm30, malgré la nécessité d'une boucle d'initialisation distincte et l'utilisation d'appels de fonction pour permuter les éléments.

Documentation: pkg/math/Rand/#Shuffle

Exemple:

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

Réponse d'Evan Shaw a un bug mineur. Si nous parcourons la tranche de l'indice le plus bas au plus élevé, pour obtenir un mélange aléatoire (pseudo) uniforme, selon le même article , nous devons choisir un entier aléatoire dans l'intervalle [i,n) par opposition à [0,n+1).

Cette implémentation fera ce dont vous avez besoin pour des entrées plus grandes, mais pour des tranches plus petites, elle effectuera un shuffle non uniforme.

Pour utiliser Rand.Intn(), nous pouvons faire:

    for i := len(slice) - 1; i > 0; i-- {
        j := Rand.Intn(i + 1)
        slice[i], slice[j] = slice[j], slice[i]
    }

en suivant le même algorithme de l'article Wikipedia.

7
heranglo

Vous pouvez peut-être également utiliser la fonction suivante:

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

approche de Raed est très rigide à cause de []interface{} en entrée. Voici une version plus pratique pour 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)
    }
}

Exemple d'utilisation:

    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]

Et aussi, n'oubliez pas que ne petite copie vaut mieux qu'une petite dépendance

1
Joseph Buchma

Lorsque vous utilisez le math/Rand package, n'oubliez pas de définir une source

// 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.

J'ai donc écrit une fonction Shuffle qui prend cela en considération:

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

Et pour l'utiliser:

source := Rand.NewSource(time.Now().UnixNano())
array := []interface{}{"a", "b", "c"}

Shuffle(array, source) // [c b a]

Si vous souhaitez l'utiliser, vous pouvez le trouver ici https://github.com/shomali11/util

1
Raed Shomali

Utilisez Shuffle () dans le math/Rand bibliothèque.

Voici un exemple:

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

Puisqu'il vient du math/Rand bibliothèque dont elle a besoin pour être initialisée. Voir ici pour plus de détails.

0
stwykd