Golang でランダム値を扱うパッケージは以下の 2 つが用意されています。
- math/rand … 擬似乱数生成器 (pseudo-random number generator)。初期化のための Seed によって生成される一連の乱数が決定するため、再現性がある。高速な生成が可能だが、生成されるランダム値が予測され得るため、暗号系技術での使用には適さない。
- crypto/rand … 暗号論的擬似乱数生成器(CSPRNG: cryptographically secure pseudo random number generator)。生成されるランダム値を予測するのが困難で、暗号系技術での使用に適している。例えば、秘密鍵の生成や、Nonce 値の生成に用いることができる。
math/rand
に比べ、crypto/rand
でのランダム値生成は時間がかかる。
math/rand による乱数生成
math/rand
パッケージのランダム生成器 (*rand.Rand
) は、rand.New
コンストラクタで生成します。
典型的には、現在時刻を元にしたシードを与えて初期化します。
あるいは、rand.Seed
関数で、トップレベル関数用のシードを設定することもできます。
こちらの方法を使う場合は、*rand.Rand
インスタンスを生成する必要はありません。
math/rand
パッケージは、次のような乱数生成関数を提供しています。
メソッド | 説明 | 戻り値の型 | 値の範囲 |
---|---|---|---|
Int31() | 0 以上の 31 ビット整数 | int32 | 0 〜 2,147,483,647 |
Int31n(n) | 0 以上 n 未満の 31 ビット整数 | int32 | 0 〜 n (n ≧ 1) |
Uint32() | 0 以上の 32 ビット整数 | uint32 | 0 〜 4,294,967,295 |
Int63() | 0 以上の 63 ビット整数 | int64 | 0 〜 9,223,372,036,854,775,807 |
Int63n(n) | 0 以上 n 未満の 63 ビット整数 | int64 | 0 〜 n (n ≧ 1) |
Uint64() | 0 以上の 64 ビット整数 | uint64 | 0 〜 18,446,744,073,709,551,615 |
Int() | 0 以上の整数(少なくとも 32 ビット) | int | 0 〜 システム依存 |
Intn(n) | 0 以上 n 未満の整数(少なくとも 32 ビット) | int | 0 〜 n (n ≧ 1) |
Float32() | 浮動小数点数(float32 型) | float32 | [0.0, 1.0) (0.0 以上 1.0 未満) |
Float64() | 浮動小数点数(float64 型) | float64 | [0.0, 1.0) (0.0 以上 1.0 未満) |
Read(p []byte)
メソッドを使用すると、任意の長さのバイト配列を生成することができます。
crypto/rand による乱数生成
予測の難しい乱数値が欲しいときは、math/rand
パッケージの代わりに crypto/rand
を使用します。
パッケージ内部の乱数生成には、Linux 系 OS では /dev/urandom
や getentropy(2)
、Windows では RtlGenRandom
API が使われています。
次の例では、rand.Int
関数を使って、0 以上 n 未満のランダム整数 (*big.Int
) を生成しています。
シードによる初期化は必要ありません。
package main
import (
"crypto/rand"
"fmt"
"math/big"
)
func main() {
// 0 以上 n 未満のセキュアなランダム整数を生成
var n int64 = 1_000_000_000
val, err := rand.Int(rand.Reader, big.NewInt(n))
if err != nil {
panic(err)
}
fmt.Println(val.Int64()) //=> 721357881
}
crypto/rand
パッケージは、math/rand
ほど柔軟なランダム生成関数を備えていませんが、encoding/binary
パッケージと組み合わせて使用すると、任意の型にランダム値を詰めることができます。
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
)
func main() {
var v int32 // 任意の型を指定可能
binary.Read(rand.Reader, binary.LittleEndian, &v)
fmt.Println(v) //=> 2017071203
}
math/rand
と同様、crypto/rand
でも指定した長さのバイト配列を生成することができます。
func main() {
bytes := make([]byte, 4)
rand.Read(bytes)
fmt.Println(bytes) //=> [235 16 197 104]
}
(応用)crypto/rand で math/rand のシードを生成する
現在時刻を math/rand
のシードに使うのは抵抗があるけど、crypto/rand
ほどセキュアな乱数は必要ないという場合は、シードの生成のみを crypto/rand
で行うという方法があります。
(応用)ランダムな文字列を生成する
次の RandomId
関数は、指定した長さのランダムな文字列を生成します(例: m6t2j7a
)。