Случайное число в Go
package main
import (
"fmt"
"math/rand/v2"
)
func main() {
dice := rand.IntN(6) + 1
fmt.Println("на кубике:", dice)
x := 10 + rand.IntN(11)
fmt.Println("от 10 до 20:", x)
p := rand.Float64()
fmt.Printf("от 0 до 1: %.3f\n", p)
}Вывод при каждом запуске разный, например:
на кубике: 4
от 10 до 20: 17
от 0 до 1: 0.618rand.IntN(n)— целое от0доn-1включительно.rand.IntN(6) + 1— от 1 до 6.- Число от
aдоbвключительно —a + rand.IntN(b-a+1). rand.Float64()— дробное от 0 (включительно) до 1 (не включая 1).rand.N(n)работает с любым целым типом, например со временем:rand.N(5 * time.Second)— случайная пауза до пяти секунд.
n должно быть больше нуля: rand.IntN(0) вызывает панику — аварийную остановку программы.
Перемешать и выбрать
package main
import (
"fmt"
"math/rand/v2"
)
func main() {
names := []string{"Анна", "Борис", "Вера", "Глеб"}
rand.Shuffle(len(names), func(i, j int) {
names[i], names[j] = names[j], names[i]
})
fmt.Println("порядок выступлений:", names)
winner := names[rand.IntN(len(names))]
fmt.Println("победитель:", winner)
}rand.Shuffle переставляет элементы на месте: функция, которую ей передают, меняет местами два элемента с номерами i и j. rand.Perm(n) возвращает числа от 0 до n-1 в случайном порядке.
math/rand и math/rand/v2
Пакет math/rand/v2 появился в Go 1.22. В старом коде встречается math/rand — отличия:
| math/rand | math/rand/v2 |
|---|---|
rand.Intn(n) | rand.IntN(n) |
rand.Int63() | rand.Int64() |
rand.Seed(…) в начале программы | не нужен |
Генератор в math/rand/v2 при запуске сам получает случайное начальное значение (сид), поэтому числа каждый раз новые. В старых статьях советуют rand.Seed(time.Now().UnixNano()) — с Go 1.20 это не нужно и в math/rand, а сама функция Seed объявлена устаревшей.
Одна и та же последовательность
Для тестов и отладки бывает нужно, чтобы «случайные» числа при каждом запуске повторялись. Тогда создают свой генератор с постоянным сидом:
r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.IntN(100), r.IntN(100))С одинаковыми числами в NewPCG последовательность всегда одинаковая.
Пароли, токены, ключи — только crypto/rand
math/rand/v2 подходит для игр, перемешивания и симуляций, но не для безопасности: его последовательность можно предсказать. Для паролей, токенов (секретных строк для входа и доступа) и ключей используют crypto/rand:
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
)
func main() {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(b))
}Программа печатает 32 случайных шестнадцатеричных символа — подойдёт как токен.
Оба пакета называются rand. Если в одном файле нужны оба, одному дайте другое имя: crand "crypto/rand" — и вызывайте crand.Read(b).