xáo trộn mảng trong cờ vây


82

Tôi đã cố dịch mã Python sau sang Go

import random

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

nhưng thấy phiên bản Go của tôi dài dòng và khó xử vì không có chức năng xáo trộn và tôi phải triển khai các giao diện và chuyển đổi kiểu.

Phiên bản cờ vây thành ngữ của mã của tôi sẽ là gì?


2
Câu hỏi này có cách triển khai shuffle (): Xử lý mảng trong cờ vây .
Sjoerd

Câu trả lời:


96

Vì danh sách của bạn chỉ là các số nguyên từ 1 đến 25, bạn có thể sử dụng Perm :

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

Lưu ý rằng sử dụng một hoán vị được cho bởi rand.Permlà một cách hiệu quả để xáo trộn bất kỳ mảng nào.

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

Tôi không chắc liệu phương thức Perm có thay đổi kể từ câu trả lời này hay không, nhưng nó trả về "hoán vị giả ngẫu nhiên của các số nguyên [0, n)". Trong trường hợp này, kết quả sẽ là một hoán vị của 0 thành 24.
JayJay

1
@JayJay đó là lý do tại sao các con số được tăng lên (một giải pháp khác sẽ là chỉ thay đổi 0 thành 25).
Denys Séguret

1
Tiếp tục cuộn xuống, tính năng này hiện đã được hỗ trợ trong 1.10: stackoverflow.com/a/46185753/474189
Duncan Jones

101

Câu trả lời của dystroy là hoàn toàn hợp lý, nhưng cũng có thể xáo trộn mà không phân bổ thêm bất kỳ lát cắt nào.

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

Xem bài viết Wikipedia này để biết thêm chi tiết về thuật toán. rand.Permthực sự cũng sử dụng thuật toán này trong nội bộ.


4
Tôi hiểu đây là phiên bản "từ trong ra ngoài" trong bài báo, và bạn bỏ qua i!=jkiểm tra?
Matt Joiner

Nhìn vào trang Wikipedia, đây có vẻ là "thuật toán hiện đại" (biến thể đầu tiên). Phiên bản "từ trong ra ngoài" dường như lưu trữ kết quả trong một mảng mới, thay vì thực hiện xáo trộn tại chỗ.
jochen

46

Kể từ 1.10 Go bao gồm một chức năng xáo trộn Fisher-Yates chính thức .

Tài liệu: pkg/math/rand/#Shuffle

toán học / rand: thêm Shuffle

Shuffle sử dụng thuật toán Fisher-Yates.

Vì đây là API mới, nó cho chúng tôi cơ hội sử dụng Int31ntriển khai nhanh hơn nhiều mà hầu như tránh được sự phân chia.

Kết quả BenchmarkPerm30ViaShufflelà nhanh hơn khoảng 30% BenchmarkPerm30, mặc dù yêu cầu vòng lặp khởi tạo riêng và sử dụng các lệnh gọi hàm để hoán đổi các phần tử.

Xem thêm CL 51891 gốc

Đầu tiên, như nhận xét của shelll :

Đừng quên gieo hạt ngẫu nhiên, hoặc bạn sẽ luôn nhận được cùng một đơn hàng.
Ví dụrand.Seed(time.Now().UnixNano()

Thí dụ:

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 Cảm ơn bạn. Tôi đã bao gồm liên kết này trong câu trả lời.
VonC

3
Đừng quên gieo hạt ngẫu nhiên, hoặc bạn sẽ luôn nhận được cùng một đơn hàng. Ví dụ rand.Seed(time.Now().UnixNano()).
shelll

@shelll Cảm ơn bạn. Tôi đã bao gồm nhận xét của bạn trong câu trả lời để hiển thị nhiều hơn.
VonC

7

Câu trả lời của Evan Shaw có một lỗi nhỏ. Nếu chúng ta lặp qua lát cắt từ chỉ số thấp nhất đến cao nhất, để có được một lần xáo trộn ngẫu nhiên (giả) đồng nhất, theo cùng một bài báo , chúng ta phải chọn một số nguyên ngẫu nhiên từ khoảng [i,n) ngược lại với[0,n+1) .

Việc triển khai đó sẽ thực hiện những gì bạn cần đối với đầu vào lớn hơn, nhưng đối với các lát nhỏ hơn, nó sẽ thực hiện xáo trộn không đồng nhất.

Để tận dụng rand.Intn(), chúng ta có thể làm:

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

theo cùng một thuật toán từ bài viết Wikipedia.


Nếu một câu trả lời có lỗi, hãy chỉnh sửa câu trả lời sai, thay vì viết thêm một câu trả lời khác.
Jacob Marble

2

Có thể bạn cũng có thể sử dụng chức năng sau:

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

Khi sử dụng math/randgói, đừng quên đặt nguồ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.

Vì vậy, tôi đã viết một Shufflehàm xem xét điều này:

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

Và để sử dụng nó:

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

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

Nếu bạn muốn sử dụng nó, bạn có thể tìm thấy nó tại đây https://github.com/shomali11/util


1

Cách tiếp cận của Raed rất không linh hoạt vì []interface{}đầu vào. Đây là phiên bản thuận tiện hơn cho 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)
    }
}

Ví dụ sử dụng:

    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]

Và cũng đừng quên rằng sao chép một chút sẽ tốt hơn một chút phụ thuộc


1

Sử dụng Shuffle () từ math/randthư viện.

Đây là một ví dụ:

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

Vì nó đến từ math/randthư viện nên nó cần được seed. Xem tại đây để biết thêm chi tiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.