Go'da dizi karıştır


82

Aşağıdaki Python kodunu Go'ya çevirmeyi denedim

import random

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

ancak Go sürümümü uzun ve garip buldum çünkü karıştırma işlevi yok ve arayüzleri uygulamak ve türleri dönüştürmek zorunda kaldım.

Kodumun deyimsel bir Go versiyonu ne olabilir?


2
Bu sorunun bir shuffle () uygulaması vardır: Go'daki Dizilerin İşlenmesi .
Sjoerd

Yanıtlar:


96

Listenizi 1 ila 25 sadece tamsayılar olduğu gibi kullanabilirsiniz Perm :

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

Tarafından verilen permütasyonu kullanmanın rand.Permherhangi bir diziyi karıştırmanın etkili bir yolu olduğunu unutmayın.

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

Bu cevaptan bu yana Perm yönteminin değişip değişmediğinden emin değilim, ancak "[0, n) tam sayılarının sözde rastgele permütasyonu" döndürüyor. Bu senaryoda, sonuç 0 ile 24 arasında bir permütasyon olacaktır.
JayJay

1
@JayJay bu yüzden sayılar artıyor (başka bir çözüm de 0'ı 25'e değiştirmek olurdu).
Denys Séguret

1
Aşağı kaydırmaya devam edin, bu artık 1.10'da kutudan destekleniyor: stackoverflow.com/a/46185753/474189
Duncan Jones

101

dystroy'un cevabı tamamen mantıklı, ancak herhangi bir ek dilim ayırmadan karıştırmak da mümkün.

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

Algoritma hakkında daha fazla ayrıntı için bu Wikipedia makalesine bakın . rand.Permaslında bu algoritmayı dahili olarak da kullanır.


4
Anladığım kadarıyla bu, makaledeki "içten dışa" versiyon ve sen i!=jçeki mi unuttun ?
Matt Joiner

Wikipedia sayfasına bakıldığında, bu "modern algoritma" (ilk değişken) gibi görünüyor. "İçten dışa" sürüm, karıştırmayı yerinde yapmak yerine sonucu yeni bir dizide depoluyor gibi görünüyor.
jochen

49

1.10 Go, resmi bir Fisher-Yates karıştırma işlevi içerir.

Dokümantasyon: pkg/math/rand/#Shuffle

math / rand: Karışık ekle

Shuffle, Fisher-Yates algoritmasını kullanır.

Bu yeni API olduğu için, bize Int31nçoğunlukla bölünmeyi engelleyen çok daha hızlı bir uygulama kullanma fırsatı veriyor .

Sonuç olarak, ayrı bir başlatma döngüsü gerektirmesine ve öğeleri değiştirmek için işlev çağrıları kullanmasına rağmen BenchmarkPerm30ViaShuffle, yaklaşık% 30 daha hızlıdır BenchmarkPerm30.

Ayrıca bkz. Orijinal CL 51891

İlk olarak yorumladı tarafından Shelll :

Rastgele tohumlamayı unutma, yoksa her zaman aynı sırayı alırsın.
Örneğinrand.Seed(time.Now().UnixNano()

Misal:

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 Teşekkürler. Bu bağlantıyı cevaba ekledim.
VonC

3
Rastgele tohumlamayı unutma, yoksa her zaman aynı sırayı alırsın. Örneğin rand.Seed(time.Now().UnixNano()).
shelll

@shelll Teşekkürler. Daha fazla görünürlük için yorumunuzu cevaba ekledim.
VonC

7

Evan Shaw'un cevabında küçük bir hata var. Düşük endeksten en yüksek dilim aracılığıyla biz yinelerler bir düzgün (sözde) rastgele karıştır almak için ise uygun aynı maddede , biz aralığından rastgele tamsayı seçmelisiniz [i,n) aksine[0,n+1) .

Bu uygulama, daha büyük girdiler için ihtiyacınız olanı yapacak, ancak daha küçük dilimler için tek tip olmayan bir karıştırma gerçekleştirecektir.

Kullanmak için rand.Intn()şunları yapabiliriz:

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

Wikipedia makalesinden aynı algoritmayı takip ederek.


Bir yanıtta hata varsa, başka bir yanıt yazmak yerine yanlış yanıtı düzenleyin.
Jacob Marble

2

Belki şu işlevi de kullanabilirsiniz:

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/randPaketi kullanırken, bir kaynak ayarlamayı unutmayın

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

Ben de Shufflebunu dikkate alan bir fonksiyon yazdım :

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

Ve kullanmak için:

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

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

Kullanmak isterseniz, burada bulabilirsiniz https://github.com/shomali11/util


1

Raed'in yaklaşımı[]interface{} girdi olması nedeniyle çok esnek değil . İşte go> = 1.8 için daha uygun sürüm :

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

Örnek kullanım:

    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]

Ayrıca, biraz kopyalamanın biraz bağımlılıktan daha iyi olduğunu unutmayın.


1

Kullanım Shuffle () den math/randkütüphanede.

İşte bir örnek:

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

O geldiğinden math/randkütüphanede o seribaşı gerekmektedir. Daha fazla ayrıntı için buraya bakın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.