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.