プログラミング
記事内に商品プロモーションを含む場合があります

rand.Seedが非推奨になったGo1.20以降の乱数シードの設定方法

Aru

Go1.20以降、rand.Seed()で乱数を初期化する方法が非推奨となりワーニング(warning)が出力されるようになりました。この記事では、新たに推奨されているrand.New()を用いた乱数シードの設定方法について解説します。

rand.Seedで警告表示(ワーニング)

私は普段Visual Studio Code(VSCode)でコーディングしています。VSCodeでコーディングしていると、rand.Seed()の行に以下のようなワーニング(warning)が出力されるようになりました。

上記の警告の一部の行を抜粋したのが以下になります。

rand.Seed is deprecated: As of Go 1.20 there is no reason to call Seed with a random value. Programs that call Seed with a known value to get a specific sequence of results should use New(NewSource(seed)) to obtain a local random generator.

メッセージを読むと、「Go1.20からはSeedを呼ばずに、New(NewSource(seed))を使え」ということみたいです。

この記事では、rand.Seed()の代替手段のrand.New(NerSource(seed))について解説します。

rand.New(NewSource(Seed))を使った乱数発生

rand.New(rand.NewSource(seed))では、乱数ジェネレータを作成して乱数を発生させます。

以下、seedを値を指定して設定する場合と、time関数で設定する方法の2つを紹介します。

seedを設定する場合

rand.Newで乱数ジェネレータを作成し、作成したジェネレータを使って乱数を発生させます(r.Intnの部分)。

このように、シードを直接与えることで毎回同じ乱数列を発生できます。直接指定する方法は、主に「乱数の再現性」を確保したい場合に利用します。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var r *rand.Rand
	r = rand.New(rand.NewSource(42))

	fmt.Println(r.Intn(10))
}

time関数を利用して乱数を発生させる場合

毎回、乱数のパターンを変更したい場合によく使われる手法が、time関数を利用する方法です。

rand.New()time関数を利用する場合は、以下のように記述します。これで、現時刻を使った乱数シードの初期化を行うことができます。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var r *rand.Rand
	r = rand.New(rand.NewSource(time.Now().UnixNano()))

	fmt.Println(r.Intn(10))
}

Goのrand.Randの関数の一覧

rand.New()を使って、乱数発生器のオブジェクトを生成したら、生成したオブジェクトを使って乱数の発生を行います。以下は、rand.Randで発生できる乱数の一覧(関数一覧)です。

関数名説明
Float32()半開区間 [0.0,1.0) の擬似乱数を float32 として返す
Float64()半開区間 [0.0,1.0) の擬似乱数を float64 として返す
Int()負でない疑似乱数 int を返す
Intn(n int)半開区間 [0,n) の非負の擬似乱数を int として返す
Int31()非負の擬似ランダム 31 ビット整数を int32 として返す
Int31n(n int32)半開区間 [0,n) の非負の擬似乱数を int32 として返す
Int63()負でない疑似ランダム 63 ビット整数を int64 として返す
Int63n(n int64)半開区間 [0,n) の非負の擬似乱数を int64 として返す
NormFloat64()標準正規分布 (平均 = 0、標準偏差 = 1) で、-math.MaxFloat64 から +math.MaxFloat64 までの範囲の正規分布した float64 を返す
Perm(n int)n 個の整数のスライスとして、半開区間 [0,n) の整数の擬似ランダム順列を返す
Shuffle(n int, swap func(i, j int))要素の順序を擬似ランダム化する
Uint32()疑似ランダム 32 ビット値を uint32 として返す
Uint64()疑似ランダム 64 ビット値を uint64 として返す

基本的に、発生させたい乱数の種類に応じて関数を呼び出します。rand.New()で生成したオブジェクト名がrの場合、int型の乱数を発生させたければn = r.Int()とすれば、生成した乱数がnに代入されます。

以下では、上記の中では少しだけ特殊なShuffleについて使い方を解説します。

Go言語のrand.Shuffleの使い方

Shuffleの定義は、Shuffle(n int, swap func(i, j int))となっていて、シャッフル対象の配列等を渡す部分がありません

この関数は以下のような形式で利用します(func()の部分に、配列の入れ替えの処理を書く形です)。

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	var r *rand.Rand
	r = rand.New(rand.NewSource(42))

	const N = 10
	a := make([]int, 0)
	for i := 0; i < N; i++ {
		a = append(a, i)
	}
	fmt.Println(a)
	// [0 1 2 3 4 5 6 7 8 9]

	r.Shuffle(N, func(i, j int) {
		a[i], a[j] = a[j], a[i]
	})

	fmt.Println(a)
	// [2 5 7 9 6 8 1 4 0 3]
}

少しわかりにくいかもしれませんが、この方法は応用範囲が広いです。

例えば5~9まで(5以上の値)をシャッフルしたくない場合は、以下のように記述すれば実現できます。

	r.Shuffle(N, func(i, j int) {
		if i < 5 && j < 5 {
			a[i], a[j] = a[j], a[i]
		}
	})

	fmt.Println(a)
	// [2 0 4 3 1 5 6 7 8 9]

if文により5未満しか置換されないため、結果として0~4まではシャッフルされ、5以上はシャッフルされません。

応用範囲は広いと思います

まとめ

Go言語をアップデートしたところ、VSCodeでrand.Seed()にワーニングが発生したので調べてみました。参考になれば幸いです。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました