経緯
スマホゲーの開発やってると、「決められた範囲からランダムに選択する」みたいな実装にあたることが多い。
で毎回ちょっと調べて納得して実装して忘れて・・・を繰り返しがちで時間もったいないのでまとめておく。
ランダムに1つ選択
※ math/randパッケージ使う前提
// ランダムに選択する対象を配列に詰めておく
targetList := []int{100, 67, 31, 45, 87}
// 乱数生成器のインスタンスを生成(シード値は現在日時)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// 乱数を生成(0 〜 len(targetList) - 1の範囲で)
randIdx := r.Intn(len(targetList))
// 対象から1つランダムに選択
randVal := targetList[randIdx]
fmt.Printf("randVal: %v\n", randVal)
(Go Playgroundで何回実行しても同じになってしまう。これはGo Playgroundでは、time.Now()が固定値で返ってくるから)
補足
-
シード値を変えないと毎回同じ乱数列になってしまうので注意
-
Intn(n int)
メソッドは、0 〜 n - 1の整数をランダムにかえしてくれるメソッド -
生成される乱数の予測を困難にして安全性を高めたい場合には、
crypto/rand
を使用した方がいいらしい(※でもmath/randに比べて遅くはなるらしい)。
This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package.
ランダムに複数選択(重複許さない)
重複を許して複数選択するなら↑の処理を繰り返せばいいだけだけれども、重複を許さない場合は、配列をシャッフルして前から複数個取り出すようにしてしまうのが楽。(Shuffle(n int, swap func(i, j int))
メソッドを利用。↓の処理だと配列が破壊的に変更されることに注意)
// ランダムに選択する対象を配列に詰めておく
targetList := []int{100, 67, 31, 45, 87}
// 乱数生成器のインスタンスを生成(シード値は現在日時)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// 配列を破壊的にシャッフル
r.Shuffle(len(targetList), func(i, j int) {
targetList[i], targetList[j] = targetList[j], targetList[i]
})
// 前から3個取得
randVal, randVal2, randVal3 := targetList[0], targetList[1], targetList[2]
fmt.Printf("randVal: %v\nrandVal2: %v\nrandVal3: %v\n", randVal, randVal2, randVal3)
注意点として、↑のコードのままだと要素数が0 or 1の場合には index out of range エラーが発生するため対策が必要
参考