Go言語の乱数生成について解説
Go言語で乱数を生成する方法をシンプルに解説します。
標準ライブラリのmath/rand
パッケージを使い、シードの設定や乱数生成の基本的な使い方を取り上げます。
開発環境が整っている方向けに、すぐに試せる内容となっています。
基本的な乱数生成の利用
math/randパッケージの概要
Go言語で乱数を扱う際に最もよく使われるのが、math/rand
パッケージです。
math/rand
は高速な疑似乱数生成器を提供しており、シミュレーションやテストなどで広く利用されます。
このパッケージでは、内部で線形合同法などの手法を用いて乱数を生成し、シードの設定によって再現性や毎回異なる系列の乱数を得ることが可能です。
パッケージのインポートと基本的な関数
math/rand
パッケージを利用するためには、プログラムの冒頭でインポートする必要があります。
基本的な関数のひとつにrand.Intn(n)
があり、これは0
からn-1
までの整数乱数を返します。
以下はシンプルな乱数生成のサンプルコードです。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 0から9までの整数乱数を生成
randomInt := rand.Intn(10)
fmt.Println("生成された整数乱数:", randomInt)
}
生成された整数乱数: 7
シードの設定方法
現在時刻を利用したシードの設定
乱数を毎回変化させるためにはシード値の調整が必要です。
現在時刻のナノ秒単位の値をシードとして設定することで、プログラム実行時に異なる乱数系列が生成されます。
以下のサンプルコードは、time.Now().UnixNano()
を用いたシード設定の例です。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
"time" // 時間操作のパッケージ
)
func main() {
// 現在時刻のナノ秒をシードに設定
seedValue := time.Now().UnixNano()
rand.Seed(seedValue)
// シード設定後、0から99までの乱数を生成
randomInt := rand.Intn(100)
fmt.Println("現在時刻をシードにした乱数:", randomInt)
}
現在時刻をシードにした乱数: 42
再現性を考慮したシード設定
開発やテストの際、同じ乱数系列を再現させたい場合があります。
その場合は、固定値のシードを利用する方法が有効です。
下記コードでは、あらかじめ決められたシード値を使用することで、毎回同じ乱数系列が生成される例を示しています。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 固定のシード値を設定
fixedSeed := int64(12345)
rand.Seed(fixedSeed)
// 再現性のある乱数を生成
randomInt := rand.Intn(50)
fmt.Println("固定シードによる乱数:", randomInt)
}
固定シードによる乱数: 33
整数型および浮動小数点型乱数生成
整数型乱数の生成方法
整数型の乱数生成は主にrand.Intn()
やrand.Int()
を利用します。
rand.Intn(n)
は、指定した上限n
に対して0
からn-1
の乱数を生成します。
以下に、整数型乱数生成の例を示します。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 0から99までの整数乱数を生成
randomInt := rand.Intn(100)
fmt.Println("整数型の乱数:", randomInt)
}
整数型の乱数: 27
浮動小数点数乱数の生成方法
浮動小数点数の乱数は、rand.Float64()
を利用することで生成できます。
この関数は、
もし特定の範囲に調整する場合は、拡大縮小やシフトを行うことが可能です。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 0.0以上1.0未満の乱数を生成
randomFloat := rand.Float64()
fmt.Println("浮動小数点型の乱数:", randomFloat)
}
浮動小数点型の乱数: 0.3745401188473625
高度な乱数生成の応用
範囲指定による乱数生成
整数型の範囲指定
整数型の乱数生成で特定の範囲を指定するには、rand.Intn()
の結果にオフセットを加える方法がよく使われます。
任意の範囲
以下の例では、範囲[10, 20]の乱数を生成しています。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 範囲 [10, 20] の整数乱数を生成
min := 10
max := 20
randomInRange := rand.Intn(max - min + 1) + min
fmt.Println("範囲指定された整数乱数:", randomInRange)
}
範囲指定された整数乱数: 15
小数点型の範囲指定
小数点型の場合、まずrand.Float64()
で生成された
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
)
func main() {
// 範囲 [5.0, 10.0) の浮動小数点乱数を生成
a := 5.0
b := 10.0
randomInRange := rand.Float64()*(b - a) + a
fmt.Println("範囲指定された浮動小数点乱数:", randomInRange)
}
範囲指定された浮動小数点乱数: 7.234567891234
並行処理を用いた乱数生成の効率化
Goルーチンの活用による処理分散
Go言語の特徴である並行処理機能を利用することで、複数の乱数生成処理を同時に実行可能です。
go
ルーチンを使い、生成した乱数の値をチャネルで集約することで、効率化が図れます。
以下のサンプルコードは、5つのGoルーチンで同時に乱数を生成し、その結果をまとめて出力する例です。
package main
import (
"fmt"
"math/rand" // 乱数生成のパッケージ
"sync" // 複数ゴルーチンの同期用
"time" // 時間操作のパッケージ
)
func main() {
// 現在時刻をシードとして設定
rand.Seed(time.Now().UnixNano())
const routines = 5
results := make(chan int, routines)
var wg sync.WaitGroup
wg.Add(routines)
// 複数のゴルーチンで乱数生成を実行
for i := 0; i < routines; i++ {
go func() {
defer wg.Done()
// 各ゴルーチンで0から99までの乱数を生成
results <- rand.Intn(100)
}()
}
wg.Wait()
close(results)
// チャネルから生成された乱数を出力
for randomInt := range results {
fmt.Println("Goルーチンで生成された乱数:", randomInt)
}
}
Goルーチンで生成された乱数: 23
Goルーチンで生成された乱数: 87
Goルーチンで生成された乱数: 45
Goルーチンで生成された乱数: 12
Goルーチンで生成された乱数: 68
乱数生成の注意点と対策
シード設定に関する留意点
シード再利用時の問題点
複数回同じシード値を利用すると、毎回同じ乱数系列が生成されるため、再現性が強く出過ぎる場合があります。
固定シードはテスト時には有用ですが、意図せぬ偏りを生む可能性があるため、用途に応じたシード管理が必要です。
予期しない乱数の偏りへの対策
乱数生成アルゴリズムは、内部計算の精度やシードの更新方法などにより、意外な偏りが生じることがあります。
そのため、十分なシードの更新や、複数の乱数生成アルゴリズムを試すなどして、偏りを最小限に抑える工夫が求められます。
セキュリティ面への配慮
暗号関連パッケージとの使い分け
math/rand
パッケージは疑似乱数生成器であるため、セキュリティを重視する場合には適していません。
そのため、暗号用途や予測不可能な乱数が必要な状況では、crypto/rand
パッケージを利用し、パスワードやトークンの生成などに対応することが推奨されます。
まとめ
この記事では、Go言語の乱数生成の基本的な使い方からシードの設定方法、整数型および浮動小数点型の生成、高度な応用や注意点まで詳しく解説しました。
乱数生成の各手法とサンプルコードを通して、その応用範囲や注意すべき点を押さえることができます。
ぜひ実際にコードを試して、より効果的な乱数生成の実装を進めてみてください。