Глиф Учебники

Случайное число в Go

Справочник · Go · 1 минута чтения

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.618

n должно быть больше нуля: rand.IntN(0) вызывает панику — аварийную остановку программы.

Перемешать и выбрать

Go
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/randmath/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 объявлена устаревшей.

Одна и та же последовательность

Для тестов и отладки бывает нужно, чтобы «случайные» числа при каждом запуске повторялись. Тогда создают свой генератор с постоянным сидом:

Go
r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.IntN(100), r.IntN(100))

С одинаковыми числами в NewPCG последовательность всегда одинаковая.

Пароли, токены, ключи — только crypto/rand

math/rand/v2 подходит для игр, перемешивания и симуляций, но не для безопасности: его последовательность можно предсказать. Для паролей, токенов (секретных строк для входа и доступа) и ключей используют crypto/rand:

Go
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).