matriz aleatoria en Go


82

Intenté traducir el siguiente código de Python a Go

import random

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

pero encontré mi versión de Go larga e incómoda porque no hay función de reproducción aleatoria y tuve que implementar interfaces y convertir tipos.

¿Cuál sería una versión idiomática de Go de mi código?


2
Esta pregunta tiene una implementación shuffle (): Tratamiento de matrices en Go .
Sjoerd

Respuestas:


96

Como su lista es solo los números enteros del 1 al 25, puede usar Perm :

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

Tenga en cuenta que usar una permutación dada por rand.Permes una forma efectiva de mezclar cualquier arreglo.

dest := make([]int, len(src))
perm := rand.Perm(len(src))
for i, v := range perm {
    dest[v] = src[i]
}

No estoy seguro de si el método Perm ha cambiado desde esta respuesta, pero devuelve "una permutación pseudoaleatoria de los enteros [0, n)". En este escenario, el resultado sería una permutación de 0 a 24.
JayJay

1
@JayJay es por eso que los números se incrementan (otra solución hubiera sido simplemente cambiar de 0 a 25).
Denys Séguret

1
Continúe desplazándose hacia abajo, esto ahora se admite de inmediato
Duncan Jones

101

La respuesta de dystroy es perfectamente razonable, pero también es posible mezclar sin asignar cortes adicionales.

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

Consulte este artículo de Wikipedia para obtener más detalles sobre el algoritmo. rand.Permen realidad también utiliza este algoritmo internamente.


4
Supongo que esta es la versión "de adentro hacia afuera" del artículo, ¿y eliges el i!=jcheque?
Matt Joiner

Mirando la página de Wikipedia, este parece ser el "algoritmo moderno" (primera variante). La versión "de adentro hacia afuera" parece almacenar el resultado en una nueva matriz, en lugar de hacer la mezcla en su lugar.
jochen

46

Desde 1.10 Go incluye una función de reproducción aleatoria oficial de Fisher-Yates .

Documentación: pkg/math/rand/#Shuffle

math / rand: agregar Shuffle

Shuffle utiliza el algoritmo de Fisher-Yates.

Dado que se trata de una nueva API, nos brinda la oportunidad de utilizar una Int31nimplementación mucho más rápida que en su mayoría evita la división.

Como resultado, BenchmarkPerm30ViaShufflees aproximadamente un 30% más rápido que BenchmarkPerm30, a pesar de requerir un ciclo de inicialización separado y usar llamadas a funciones para intercambiar elementos.

Vea también el original CL 51891

En primer lugar, como se ha comentado por shelll :

No olvides sembrar al azar, o siempre obtendrás el mismo orden.
Por ejemplorand.Seed(time.Now().UnixNano()

Ejemplo:

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 Gracias. He incluido este enlace en la respuesta.
VonC

3
No olvides sembrar al azar, o siempre obtendrás el mismo orden. Por ejemplo rand.Seed(time.Now().UnixNano()).
shelll

@shelll Gracias. He incluido su comentario en la respuesta para mayor visibilidad.
VonC

7

La respuesta de Evan Shaw tiene un error menor. Si iteramos a través del segmento desde el índice más bajo al más alto, para obtener una mezcla aleatoria uniforme (pseudo) aleatoria, de acuerdo con el mismo artículo , debemos elegir un número entero aleatorio del intervalo [i,n) en lugar de[0,n+1) .

Esa implementación hará lo que necesita para entradas más grandes, pero para cortes más pequeños, realizará una mezcla no uniforme.

Para utilizar rand.Intn(), podemos hacer:

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

siguiendo el mismo algoritmo del artículo de Wikipedia.


Si una respuesta tiene un error, edite la respuesta incorrecta, en lugar de escribir otra respuesta.
Jacob Marble

2

Quizás también puedas usar la siguiente función:

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

Cuando use el math/randpaquete, no olvide establecer una fuente

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

Entonces escribí una Shufflefunción que toma esto en consideración:

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

Y para usarlo:

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

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

Si desea utilizarlo, puede encontrarlo aquí https://github.com/shomali11/util


1

El enfoque de Raed es muy inflexible debido a su []interface{}entrada. Aquí hay una versión más conveniente para 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)
    }
}

Uso de ejemplo:

    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]

Y además, no olvides que un poco de copia es mejor que un poco de dependencia.


1

Utilice Shuffle () de la math/randbiblioteca.

He aquí un ejemplo:

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

Dado que proviene de la math/randbiblioteca, es necesario sembrar. Consulte aquí para obtener más detalles.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.