Go

Goでrandを使うときは忘れずにSeedを設定しないといけない

More than 1 year has passed since last update.

冷静に考えてみれば当たり前の話。

最近の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())
}

皆様もよりよい乱数生活を。