Тип rand.Rand

Тип rand.Rand представляет собой генератор псевдослучайных чисел, который хранит собственное состояние. Это позволяет создавать несколько независимых генераторов с разными начальными значениями. В отличие от глобального генератора, использование собственного экземпляра rand.Rand дает контроль над последовательностью случайных чисел и позволяет получать воспроизводимые результаты при одинаковом начальном значении.

Создание экземпляра

Для создания нового генератора используется функция rand.New, которая принимает источник rand.Source. Обычно в качестве источника используют rand.NewSource с указанным начальным значением:

gopackage main

import (
  "fmt"
  "math/rand"
)

func main() {
  source := rand.NewSource(42)
  r := rand.New(source)
  fmt.Println(r.Intn(100))
}

Результат выполнения кода:

go"81"

Методы генерации чисел

Экземпляр rand.Rand предоставляет методы для генерации различных типов случайных чисел:

gopackage main

import (
  "fmt"
  "math/rand"
)

func main() {
  r := rand.New(rand.NewSource(1))

  fmt.Println(r.Int())      // случайное int
  fmt.Println(r.Intn(100))    // int от 0 до 99
  fmt.Println(r.Int31())    // случайное int32
  fmt.Println(r.Int63())    // случайное int64
  fmt.Println(r.Float64())    // float64 от 0.0 до 1.0
  fmt.Println(r.Float32())    // float32 от 0.0 до 1.0
  fmt.Println(r.Uint32())     // случайное uint32
  fmt.Println(r.Uint64())     // случайное uint64
}

Результат выполнения кода:

go"5577006791947779410"
"81"
"1298498081"
"5577006791947779410"
"0.6046602879796196"
"0.6046603"
"1298498081"
"5577006791947779410"

Воспроизводимость результатов

При использовании одного и того же начального значения генератор всегда выдает одинаковую последовательность чисел. Это полезно для тестирования и отладки:

gopackage main

import (
  "fmt"
  "math/rand"
)

func main() {
  r1 := rand.New(rand.NewSource(10))
  r2 := rand.New(rand.NewSource(10))

  fmt.Println(r1.Intn(1000))
  fmt.Println(r2.Intn(1000))
  fmt.Println(r1.Intn(1000))
  fmt.Println(r2.Intn(1000))
}

Результат выполнения кода:

go"81"
"81"
"887"
"887"

Независимость генераторов

Каждый экземпляр rand.Rand работает независимо от других. Это позволяет использовать разные генераторы в разных частях программы без взаимного влияния:

gopackage main

import (
  "fmt"
  "math/rand"
)

func main() {
  rA := rand.New(rand.NewSource(100))
  rB := rand.New(rand.NewSource(200))

  fmt.Println(rA.Intn(100))
  fmt.Println(rB.Intn(100))
  fmt.Println(rA.Intn(100))
  fmt.Println(rB.Intn(100))
}

Результат выполнения кода:

go"69"
"8"
"5"
"91"

Методы для работы со слайсами

Тип rand.Rand также предоставляет методы для перемешивания элементов слайса и генерации перестановок:

gopackage main

import (
  "fmt"
  "math/rand"
)

func main() {
  r := rand.New(rand.NewSource(5))

  // Перемешивание слайса
  slice := []int{1, 2, 3, 4, 5}
  r.Shuffle(len(slice), func(i, j int) {
    slice[i], slice[j] = slice[j], slice[i]
  })
  fmt.Println(slice)

  // Генерация перестановки
  perm := r.Perm(5)
  fmt.Println(perm)
}

Результат выполнения кода:

go"[2 3 5 1 4]"
"[2 3 1 0 4]"

Потокобезопасность

Важно помнить, что экземпляры rand.Rand не являются потокобезопасными. При использовании в нескольких горутинах необходимо синхронизировать доступ с помощью мьютекса или использовать отдельные генераторы для каждой горутины:

gopackage main

import (
  "fmt"
  "math/rand"
  "sync"
)

func main() {
  var mu sync.Mutex
  r := rand.New(rand.NewSource(1))

  var wg sync.WaitGroup
  for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
      defer wg.Done()
      mu.Lock()
      val := r.Intn(100)
      mu.Unlock()
      fmt.Println(val)
    }()
  }
  wg.Wait()
}

Смотрите также

  • функцию rand.New,
    которая создает новый генератор случайных чисел
  • функцию rand.NewSource,
    которая создает источник случайных чисел
  • функцию rand.Intn,
    которая генерирует случайное число от 0 до n-1
  • функцию rand.Perm,
    которая возвращает перестановку чисел от 0 до n-1