[random] 난수 생성기를 올바르게 시드하는 방법

Go에서 임의의 문자열을 생성하려고하는데 여기에 내가 작성한 코드가 있습니다.

package main

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

func main() {
    fmt.Println(randomString(10))
}

func randomString(l int) string {
    var result bytes.Buffer
    var temp string
    for i := 0; i < l; {
        if string(randInt(65, 90)) != temp {
            temp = string(randInt(65, 90))
            result.WriteString(temp)
            i++
        }
    }
    return result.String()
}

func randInt(min int, max int) int {
    rand.Seed(time.Now().UTC().UnixNano())
    return min + rand.Intn(max-min)
}

구현이 매우 느립니다. 시딩을 사용 time하면 특정 시간 동안 동일한 난수가 발생하므로 루프가 계속 반복됩니다. 코드를 어떻게 개선 할 수 있습니까?



답변

동일한 시드를 설정할 때마다 동일한 시퀀스를 얻습니다. 물론 빠른 루프에서 시드를 시간으로 설정하는 경우 동일한 시드로 여러 번 호출 할 수 있습니다.

귀하의 경우, randInt다른 값을 가질 때까지 함수를 호출 할 때 (Nano에서 반환 한) 시간이 변경되기를 기다리고 있습니다.

모든 의사 랜덤 라이브러리의 경우 주어진 시퀀스를 재현 할 필요가없는 경우 (일반적으로 디버깅 및 단위 테스트 용으로 만 수행됨)를 제외하고 프로그램을 초기화 할 때와 같이 시드를 한 번만 설정해야합니다.

그런 다음 단순히 Intn다음 임의의 정수를 얻기 위해 호출 합니다.

rand.Seed(time.Now().UTC().UnixNano())randInt 함수에서 메인의 시작 부분으로 라인을 이동하면 모든 것이 더 빨라집니다.

또한 문자열 작성을 단순화 할 수 있다고 생각합니다.

package main

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

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    fmt.Println(randomString(10))
}

func randomString(l int) string {
    bytes := make([]byte, l)
    for i := 0; i < l; i++ {
        bytes[i] = byte(randInt(65, 90))
    }
    return string(bytes)
}

func randInt(min int, max int) int {
    return min + rand.Intn(max-min)
}


답변

사람들이 왜 시간 가치를 부여하는 지 이해할 수 없습니다. 이것은 내 경험상 결코 좋은 생각이 아닙니다. 예를 들어, 시스템 클럭이 나노초로 표시 될 수 있지만 시스템의 클럭 정밀도는 나노초가 아닙니다.

이 프로그램 은 Go 놀이터에서 실행해서는 안되지만 컴퓨터에서 실행하면 예상 할 수있는 정밀도 유형에 대한 대략적인 추정치를 얻을 수 있습니다. 약 1000000ns 씩 증가하므로 1ms 씩 증가합니다. 그것은 사용되지 않는 20 비트의 엔트로피입니다. 높은 비트는 항상 일정합니다.

이것이 당신에게 중요한 정도는 다를 것이지만, 단순히 crypto/rand.Read씨앗의 소스로 사용함으로써 시계 기반의 씨앗 값의 함정을 피할 수 있습니다 . 실제 구현 자체가 고유하고 결정 론적 무작위 시퀀스로 제한되어 있어도 난수에서 찾고있는 비 결정적 품질을 제공합니다.

import (
    crypto_rand "crypto/rand"
    "encoding/binary"
    math_rand "math/rand"
)

func init() {
    var b [8]byte
    _, err := crypto_rand.Read(b[:])
    if err != nil {
        panic("cannot seed math/rand package with cryptographically secure random number generator")
    }
    math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
}

부수적으로하지만 귀하의 질문과 관련하여. rand.Source소스를 보호하는 잠금 비용이 들지 않도록이 방법을 사용 하여 직접 작성할 수 있습니다 . rand패키지 유틸리티 기능은 편리하지만 또한 동시에 사용되는 소스를 방지하기 위해 후드 잠금을 사용합니다. 필요하지 않은 경우 직접 작성하여 피할 수 Source있으며 비 동시 방식으로 사용하십시오. 그럼에도 불구하고 반복 사이에 난수 생성기를 다시 시드해서는 안되며 절대 그런 식으로 사용되도록 설계되지 않았습니다.


답변

후손을 위해 그것을 던지기 위해 : 때로는 초기 문자 세트 문자열을 사용하여 임의의 문자열을 생성하는 것이 바람직 할 수 있습니다. 사람이 직접 문자열을 입력해야하는 경우에 유용합니다. 0, O, 1 및 l을 제외하면 사용자 오류를 줄일 수 있습니다.

var alpha = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"

// generates a random string of fixed size
func srand(size int) string {
    buf := make([]byte, size)
    for i := 0; i < size; i++ {
        buf[i] = alpha[rand.Intn(len(alpha))]
    }
    return string(buf)
}

저는 보통 씨앗을 init()블록 안에 넣었습니다. 그들은 여기에 문서화되어 있습니다 : http://golang.org/doc/effective_go.html#init


답변

그래 왜 그렇게 복잡해!

package main

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

func main() {
    rand.Seed( time.Now().UnixNano())
    var bytes int

    for i:= 0 ; i < 10 ; i++{
        bytes = rand.Intn(6)+1
        fmt.Println(bytes)
        }
    //fmt.Println(time.Now().UnixNano())
}

이것은 dystroy의 코드를 기반으로하지만 내 요구에 적합합니다.

그것은 여섯 죽는다 (rands ints 1 =< i =< 6)

func randomInt (min int , max int  ) int {
    var bytes int
    bytes = min + rand.Intn(max)
    return int(bytes)
}

위의 기능은 정확히 같습니다.

이 정보가 사용되기를 바랍니다.


답변

나노초로 같은 씨앗을 두 번 얻을 확률은 얼마입니까?
어쨌든 도움을 주셔서 감사합니다. 여기에 모든 입력을 기반으로 한 최종 솔루션이 있습니다.

package main

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UTC().UnixNano())
}

// generates a random string
func srand(min, max int, readable bool) string {

    var length int
    var char string

    if min < max {
        length = min + rand.Intn(max-min)
    } else {
        length = min
    }

    if readable == false {
        char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    } else {
        char = "ABCDEFHJLMNQRTUVWXYZabcefghijkmnopqrtuvwxyz23479"
    }

    buf := make([]byte, length)
    for i := 0; i < length; i++ {
        buf[i] = char[rand.Intn(len(char)-1)]
    }
    return string(buf)
}

// For testing only
func main() {
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, false))
    println(srand(5, 7, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 4, true))
    println(srand(5, 400, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
}


답변

당신의 목표가 난수의 찌르기를 생성하는 것이라면, 매번 여러 함수 호출이나 시드 재설정과 함께 복잡하게 할 필요가 없다고 생각합니다.

가장 중요한 단계는 실제로 실행하기 전에 seed 함수를 한 번만 호출하는 것 rand.Init(x)입니다. Seed 는 제공된 seed 값을 사용하여 기본 소스를 결정적 상태로 초기화합니다. 따라서 의사 난수 생성기에 대한 실제 함수 호출 전에 한 번 호출하는 것이 좋습니다.

다음은 임의의 숫자 문자열을 만드는 샘플 코드입니다.

package main
import (
    "fmt"
    "math/rand"
    "time"
)



func main(){
    rand.Seed(time.Now().UnixNano())

    var s string
    for i:=0;i<10;i++{
    s+=fmt.Sprintf("%d ",rand.Intn(7))
    }
    fmt.Printf(s)
}

Sprintf를 사용한 이유 는 간단한 문자열 형식을 허용하기 때문입니다.

또한 In rand.Intn(7) Intn 은 [0,7)의 음수가 아닌 의사 난수를 정수로 반환합니다.


답변

@ [Denys Séguret] 님이 올바로 게시했습니다. 그러나 제 경우에는 항상 코드 아래에 새로운 시드가 필요합니다.

빠른 기능이 필요한 경우. 나는 이렇게 사용합니다.


func RandInt(min, max int) int {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return r.Intn(max-min) + min
}

func RandFloat(min, max float64) float64 {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return min + r.Float64()*(max-min)
}

출처