Goでrandを使うときは忘れずにSeedを設定しないといけない
⚠ この記事を真似してはいけません。
Go 1.20 にて自動でSeedが設定されるようになりました。rand.Seed関数も非推奨になりました。
参照: Go 1.20 Release Notes#math/rand
冷静に考えてみれば当たり前の話。 最近のLLだといいかんじに初期Seedが設定されてるので忘れていた。
Seedを明示的に設定しなかった場合
math/randのコードを見ると、次のようにSeedは 1 固定で初期化されている。
var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})
このためいつでもどこでも同じ乱数列が得られてしまう。
時刻を使ってSeedを設定する
よくあるパターン。time.Now().UnixNano()
を使う。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Int63())
}
ちゃんとした乱数をSeedに使う
Goにはcrypto/rand
というセキュアな乱数生成器があるので、Seedくらいはこちらを使ったほうがベター。
package main
import (
crand "crypto/rand"
"fmt"
"math"
"math/big"
"math/rand"
)
func main() {
seed, _ := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
rand.Seed(seed.Int64())
fmt.Println(rand.Int63())
}
せっかくなので乱数生成器もメルセンヌ・ツイスタにする
メルセンヌ・ツイスタのGo言語実装が公開されているので、使わせていただく。
package main
import (
crand "crypto/rand"
"fmt"
"math"
"math/big"
"math/rand"
"github.com/seehuhn/mt19937"
)
func main() {
seed, _ := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
rng := rand.New(mt19937.New())
rng.Seed(seed.Int64())
fmt.Println(rng.Int63())
}
皆様もよりよい乱数生活を。