随机发电机在一些电话后停止工作

时间:2017-12-01 22:34:22

标签: go random

我想要shuffle db id,以便没有id引用自己,但是使用这段代码:

package main

import (
    "log"
    "math/rand"
    "time"
)

func main() {
    seed := time.Now().UnixNano() & 999999999
    log.Print("seed: ", seed)
    rand.Seed(seed)
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}

    randomized := shufflePreventCollision(ordered)
    log.Print("Final Result")
    log.Print("ordered: ", ordered)
    log.Print("random:  ", randomized)

}

func shufflePreventCollision(ordered []int) []int {
    randomized := rand.Perm(len(ordered))
    for i, o := range ordered {
        if o == randomized[i] {
            log.Printf("Doing it again because ordered[%d] (%d) is == randomized[%d] (%d)", i, o, i, randomized[i])
            log.Print(ordered)
            log.Print(randomized)
            shufflePreventCollision(ordered)
        }
    }
    return randomized
}

我发现了一种奇怪的行为,当它经常运行时,它会在某个时刻挂起并且再也找不到非碰撞序列了。我试过了 go build -o rand_example3 rand_example3.go && time (for i in $(seq 10000) ; do ./rand_example3 ; done)

似乎永远不会结束。我在这里错过了一些理解,还是数学/兰德确实有些可疑?

1 个答案:

答案 0 :(得分:1)

  

"使用这段代码,我想要shuffle db id,以便没有   ids指自己。 [有时候]即使我放手也不会结束   跑了一个小时左右。"

tl; dr有一种更快的解决方案,速度要快一千倍。

您的代码:

slaxor.go

package main

import (
    "log"
    "math/rand"
    "time"
)

func main() {
    seed := time.Now().UnixNano() & 999999999
    log.Print("seed: ", seed)
    rand.Seed(seed)
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}

    randomized := shufflePreventCollisionSlaxor(ordered)
    log.Print("Final Result")
    log.Print("ordered: ", ordered)
    log.Print("random:  ", randomized)

}

func shufflePreventCollisionSlaxor(ordered []int) []int {
    randomized := rand.Perm(len(ordered))
    for i, o := range ordered {
        if o == randomized[i] {
            log.Printf("Doing it again because ordered[%d] (%d) is == randomized[%d] (%d)", i, o, i, randomized[i])
            log.Print(ordered)
            log.Print(randomized)
            shufflePreventCollisionSlaxor(ordered)
        }
    }
    return randomized
}

游乐场:https://play.golang.org/p/JI5rJGcAAz

代码是如何实现其目的的,也不是很明显。

终止条件是概率性的,而不是确定性的。

让我们不谈代码是否符合其目的的问题。

此基准已被修改,因此stderr受到接收器/dev/null的速度限制,而不是终端。

slaxor.bash

go build -o slaxor slaxor.go && time (for i in $(seq 10000) ; do ./slaxor 2> /dev/null ; done)

基准测试程序的执行和算法的单次执行。基准时间不一致,因为每个程序执行的伪随机种子值都会发生变化。即使我让它运行了一个小时左右,基准有时也不会结束。"

尽管有程序执行开销,但有一个更快的解决方案可以在几秒钟内运行和终止。

peterso.bash

go build -o peterso peterso.go && time (for i in $(seq 10000) ; do ./peterso 2> /dev/null ; done)

输出:

$ ./peterso.bash
real    0m5.290s
user    0m5.224s
sys     0m1.128s

$ ./peterso.bash
real    0m7.462s
user    0m7.109s
sys     0m1.922s

peterso.go

package main

import (
    "fmt"
    "log"
    "math/rand"
    "time"
)

func main() {
    seed := time.Now().UnixNano() & 999999999
    log.Print("seed: ", seed)
    r = rand.New(rand.NewSource(seed))
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}

    randomized := shufflePreventCollisionPeterSO(ordered)
    log.Print("Final Result")
    log.Print("ordered: ", ordered)
    log.Print("random:  ", randomized)
    if randomized == nil {
        err := "Shuffle Error!"
        fmt.Print(err)
        log.Fatal(err)
    }
}

var r *rand.Rand

func isNoCollision(a, b []int) bool {
    if len(a) == len(b) {
        for i, ai := range a {
            if ai == b[i] {
                return false
            }
        }
        return true
    }
    return false
}

func shufflePreventCollisionPeterSO(ordered []int) []int {
    const guard = 4 * 1024 // deterministic, finite time
    for n := 1; n <= guard; n++ {
        randomized := r.Perm(len(ordered))
        if isNoCollision(ordered, randomized) {
            return randomized
        }
    }
    return nil
}

守卫提供确定的,有限时间的终止条件。

游乐场:https://play.golang.org/p/ZT-sfDW5Mi

让我们抛开扭曲的程序执行基准,让我们看一下函数执行时间。因为这很重要,Go标准库具有testing包,用于测试和基准测试功能。

试验:

$ go test shuffle_test.go -v -count=1 -run=. -bench=!
=== RUN   TestTimeSlaxor
=== RUN   TestTimeSlaxor/1K
=== RUN   TestTimeSlaxor/2K
=== RUN   TestTimeSlaxor/3K
--- PASS: TestTimeSlaxor (13.78s)
    --- PASS: TestTimeSlaxor/1K (1.18s)
    --- PASS: TestTimeSlaxor/2K (1.27s)
    --- PASS: TestTimeSlaxor/3K (11.33s)
=== RUN   TestTimePeterSO
=== RUN   TestTimePeterSO/1K
=== RUN   TestTimePeterSO/2K
=== RUN   TestTimePeterSO/3K
=== RUN   TestTimePeterSO/1M
=== RUN   TestTimePeterSO/2M
=== RUN   TestTimePeterSO/3M
--- PASS: TestTimePeterSO (6.57s)
    --- PASS: TestTimePeterSO/1K (0.00s)
    --- PASS: TestTimePeterSO/2K (0.00s)
    --- PASS: TestTimePeterSO/3K (0.00s)
    --- PASS: TestTimePeterSO/1M (1.13s)
    --- PASS: TestTimePeterSO/2M (2.25s)
    --- PASS: TestTimePeterSO/3M (3.19s)
PASS
ok      command-line-arguments  20.347s
$ 

在快速增加的时间的一小部分中,运行3 {(3,000)次迭代的shufflePreventCollisionSlaxor,3M(3,000,000)次迭代shufflePreventCollisionPeterSO运行,这是一个超过千倍的改进。

基准:

$ go test shuffle_test.go -v -count=1 -run=! -bench=.
goos: linux
goarch: amd64
BenchmarkTimePeterSO-8   1000000      1048 ns/op      434 B/op      2 allocs/op
BenchmarkTimeSlaxor-8      10000   2256271 ns/op   636894 B/op   3980 allocs/op
PASS
ok      command-line-arguments  23.643s
$ 

很容易看出,每次迭代的shufflePreventCollisionPeterSO平均成本为1,000,000次迭代很小,为1,048纳秒,特别是与每次迭代平均2,256,271纳秒的shufflePreventCollisionSlaxor的10,000次迭代相比时

另外,请注意shufflePreventCollisionPeterSO对内存的节约使用,平均每次迭代总共分配434个字节的分配,而shufflePreventCollisionSlaxor的内存使用量大大增加,平均为3,980个分配每次迭代总共分配636,894字节。

shuffle_test.go

package main

import (
    "fmt"
    "math/rand"
    "strconv"
    "testing"
)

func shufflePreventCollisionSlaxor(ordered []int) []int {
    randomized := rand.Perm(len(ordered))
    for i, o := range ordered {
        if o == randomized[i] {
            shufflePreventCollisionSlaxor(ordered)
        }
    }
    return randomized
}

var r *rand.Rand

func isNoCollision(a, b []int) bool {
    if len(a) == len(b) {
        for i, ai := range a {
            if ai == b[i] {
                return false
            }
        }
        return true
    }
    return false
}

func shufflePreventCollisionPeterSO(ordered []int) []int {
    const guard = 4 * 1024 // deterministic, finite time
    for n := 1; n <= guard; n++ {
        randomized := r.Perm(len(ordered))
        if isNoCollision(ordered, randomized) {
            return randomized
        }
    }
    return nil
}

const testSeed = int64(60309766)

func testTime(t *testing.T, ordered, randomized []int, shuffle func([]int) []int) {
    shuffled := shuffle(ordered)
    want := fmt.Sprintf("%v", randomized)
    got := fmt.Sprintf("%v", shuffled)
    if want != got {
        t.Errorf("Error:\n  from: %v\n  want: %s\n  got:  %s\n", ordered, want, got)
    }
}

func testTimeSlaxor(t *testing.T, n int) {
    rand.Seed(testSeed)
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
    randomized := []int{3, 1, 17, 15, 10, 16, 14, 19, 7, 6, 11, 2, 0, 12, 8, 18, 13, 4, 9, 5}
    testTime(t, ordered, randomized, shufflePreventCollisionSlaxor)
    for i := 1; i < n; i++ {
        shufflePreventCollisionSlaxor(ordered)
    }
}

func TestTimeSlaxor(t *testing.T) {
    for k := 1; k <= 3; k++ {
        n := 1000 * k
        t.Run(strconv.Itoa(k)+"K", func(t *testing.T) { testTimeSlaxor(t, n) })
    }
}

func testTimePeterSo(t *testing.T, n int) {
    r = rand.New(rand.NewSource(testSeed))
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
    randomized := []int{10, 7, 15, 14, 8, 6, 18, 17, 19, 11, 5, 16, 2, 12, 1, 13, 3, 0, 9, 4}
    testTime(t, ordered, randomized, shufflePreventCollisionPeterSO)
    for i := 1; i < n; i++ {
        shufflePreventCollisionPeterSO(ordered)
    }
}

func TestTimePeterSO(t *testing.T) {
    for k := 1; k <= 3; k++ {
        n := 1000 * k
        t.Run(strconv.Itoa(k)+"K", func(t *testing.T) { testTimePeterSo(t, n) })
    }
    for m := 1; m <= 3; m++ {
        n := 1000 * 1000 * m
        t.Run(strconv.Itoa(m)+"M", func(t *testing.T) { testTimePeterSo(t, n) })
    }
}

func benchTime(b *testing.B, ordered, randomized []int, shuffle func([]int) []int) {
    shuffled := shuffle(ordered)
    want := fmt.Sprintf("%v", randomized)
    got := fmt.Sprintf("%v", shuffled)
    if want != got {
        b.Errorf("Error:\n  from: %v\n  want: %s\n  got:  %s\n", ordered, want, got)
    }
}


func BenchmarkTimePeterSO(b *testing.B) {
    b.ReportAllocs()
    r = rand.New(rand.NewSource(testSeed))
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
    randomized := []int{10, 7, 15, 14, 8, 6, 18, 17, 19, 11, 5, 16, 2, 12, 1, 13, 3, 0, 9, 4}
    benchTime(b, ordered, randomized, shufflePreventCollisionPeterSO)
    r = rand.New(rand.NewSource(testSeed))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        shufflePreventCollisionPeterSO(ordered)
    }
}


func BenchmarkTimeSlaxor(b *testing.B) {
    b.ReportAllocs()
    r = rand.New(rand.NewSource(testSeed))
    ordered := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
    randomized := []int{10, 7, 15, 14, 8, 6, 18, 17, 19, 11, 5, 16, 2, 12, 1, 13, 3, 0, 9, 4}
    benchTime(b, ordered, randomized, shufflePreventCollisionPeterSO)
    r = rand.New(rand.NewSource(testSeed))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        shufflePreventCollisionSlaxor(ordered)
    }
}

游乐场:https://play.golang.org/p/ozazWGNZsu