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