⚠ この記事を真似してはいけません。
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())
}

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