数値

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()を利用することで生成できます。

この関数は、0.0x<1.0 の範囲で乱数を生成します。

もし特定の範囲に調整する場合は、拡大縮小やシフトを行うことが可能です。

package main
import (
    "fmt"
    "math/rand" // 乱数生成のパッケージ
)
func main() {
    // 0.0以上1.0未満の乱数を生成
    randomFloat := rand.Float64()
    fmt.Println("浮動小数点型の乱数:", randomFloat)
}
浮動小数点型の乱数: 0.3745401188473625

高度な乱数生成の応用

範囲指定による乱数生成

整数型の範囲指定

整数型の乱数生成で特定の範囲を指定するには、rand.Intn()の結果にオフセットを加える方法がよく使われます。

任意の範囲 [a,b] の乱数は、rand.Intn(ba+1)+a という式で生成されます。

以下の例では、範囲[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()で生成された x (0.0x<1.0) を、目標の範囲 [a,b) に合わせて次の式で変換します。

randomFloat×(ba)+a 下記の例では、範囲[5.0, 10.0)の浮動小数点数乱数を生成しています。

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言語の乱数生成の基本的な使い方からシードの設定方法、整数型および浮動小数点型の生成、高度な応用や注意点まで詳しく解説しました。

乱数生成の各手法とサンプルコードを通して、その応用範囲や注意すべき点を押さえることができます。

ぜひ実際にコードを試して、より効果的な乱数生成の実装を進めてみてください。

関連記事

Back to top button
目次へ