私は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から返される)時間の変更を待っています。
すべての擬似ランダムライブラリに関して 、たとえば、特定のシーケンスを再現する必要がない限り、プログラムを初期化するときにシードを1回だけ設定する必要があります(通常はデバッグと単体テストのみに行われます) )。
その後、単に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)
}
後世のためにそれを放り投げる:初期の文字セット文字列を使用してランダムな文字列を生成することが望ましい場合があります。これは、人間が文字列を手動で入力することになっている場合に便利です。 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())
}
これは破壊のコードに基づいていますが、私のニーズに合っています。
死ぬ6(rands ints 1 =< i =< 6
)
func randomInt (min int , max int ) int {
var bytes int
bytes = min + Rand.Intn(max)
return int(bytes)
}
上記の機能はまったく同じものです。
この情報が役に立てば幸いです。
なぜ人々が時間の価値を与えているのか理解できません。これは私の経験では決して良い考えではありませんでした。たとえば、システムクロックはナノ秒単位で表されますが、システムのクロック精度はナノ秒ではありません。
このプログラム Goプレイグラウンドで実行する必要はありませんが、マシンで実行すると、予想できる精度の種類の大まかな見積もりが得られます。約1000000 nsの増分があるため、1ミリ秒の増分があります。これは、使用されない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
を作成し、それを非並行的に使用することで回避できます。とにかく、反復の間に乱数ジェネレーターを再シードするべきではありません。そのように使用されるように設計されていません。
ナノ秒です。同じシードを2回取得する可能性は何ですか。
とにかく、助けてくれてありがとう、ここにすべての入力に基づいた私の最終的な解決策があります。
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))
}
乱数の文字列を生成することだけが目的の場合、複数の関数呼び出しや毎回のシードのリセットで複雑にする必要はないと思います。
最も重要なステップは、実際にRand.Init(x)
を実行する前に一度だけシード関数を呼び出すことです。 Seed は、提供されたシード値を使用して、デフォルトのSourceを確定的状態に初期化します。そのため、実際の関数が擬似乱数ジェネレーターを呼び出す前に一度呼び出すことをお勧めします。
乱数の文字列を作成するサンプルコードを次に示します
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)の非負の擬似乱数をintとして返します。