math/rand/v2.New în Go

Creează generatoare proprii, cu seed: New pornește de la o sursă (NewPCG sau NewChaCha8), iar NewZipf dă valori cu distribuție Zipf.

Sintaxă
import "math/rand/v2"

func New(src Source) *Rand
func NewPCG(seed1, seed2 uint64) *PCG
func NewChaCha8(seed [32]byte) *ChaCha8
func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf

Ce face math/rand/v2.New

Funcțiile de la nivelul pachetului (rand.IntN, rand.Float64…) folosesc un generator global inițializat aleatoriu, deci dau alte numere la fiecare rulare. Când ai nevoie de aceeași secvență de fiecare dată (teste, simulări pe care vrei să le repeți, un nivel de joc generat din același seed), îți creezi un generator propriu: r := rand.New(sursa). Obiectul *rand.Rand are aceleași metode: r.IntN, r.Float64, r.Perm, r.Shuffle etc.

Sursa dă biții aleatorii. rand.NewPCG(seed1, seed2) este rapidă și primește două numere uint64 ca seed. rand.NewChaCha8(seed) primește un seed de 32 de octeți și se bazează pe cifrul ChaCha8; este algoritmul folosit și de generatorul global. Pentru același seed, ambele dau mereu aceeași secvență, în orice versiune de Go.

rand.NewZipf(r, s, v, imax) creează un generator de valori între 0 și imax cu distribuție Zipf: valorile mici sunt foarte frecvente, cele mari rare, ca popularitatea paginilor unui site sau a cuvintelor dintr-un text. Probabilitatea lui k este proporțională cu (v + k)-s. Condițiile sunt s > 1 și v ≥ 1, altfel funcția returnează nil. Valorile le iei cu z.Uint64().

Parametri

Parametru Descriere
src Pentru New: sursa de biți aleatorii (de exemplu rezultatul lui NewPCG sau NewChaCha8).
seed1, seed2 Pentru NewPCG: cele două valori de pornire.
seed Pentru NewChaCha8: valoarea de pornire, 32 de octeți.
r, s, v, imax Pentru NewZipf: generatorul folosit, exponentul (> 1), decalajul (≥ 1) și valoarea maximă.

Ce returnează

New: un *rand.Rand. NewPCG, NewChaCha8: o sursă (*rand.PCG, *rand.ChaCha8), care poate fi dată lui New. NewZipf: un *rand.Zipf sau nil dacă parametrii sunt invalizi.

Exemple

Exemplul 1: Același seed, aceeași secvență

Două generatoare create cu același seed dau exact aceleași numere. Un seed diferit dă altă secvență. Rezultatul este același la fiecare rulare.

package main

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

func main() {
    a := rand.New(rand.NewPCG(2026, 1))
    b := rand.New(rand.NewPCG(2026, 1))
    c := rand.New(rand.NewPCG(2026, 2))
    for range 3 {
        fmt.Println(a.IntN(100), b.IntN(100), c.IntN(100))
    }
}

Rezultat

46 46 16
20 20 76
87 87 3

Exemplul 2: Sursa ChaCha8 cu un seed de 32 de octeți

Seed-ul este un tablou [32]byte. Aici îl completăm dintr-un text; octeții rămași sunt 0.

package main

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

func main() {
    var seed [32]byte
    copy(seed[:], "dexblog.ro - exemplu")
    r := rand.New(rand.NewChaCha8(seed))

    elevi := []string{"Ana", "Mihai", "Ioana", "Radu", "Elena"}
    r.Shuffle(len(elevi), func(i, j int) { elevi[i], elevi[j] = elevi[j], elevi[i] })
    fmt.Println("Ordinea la oral:", elevi)
    fmt.Printf("Zarul: %d, nota bonus: %.2f\n", r.IntN(6)+1, r.Float64())
}

Rezultat

Ordinea la oral: [Radu Elena Ana Ioana Mihai]
Zarul: 6, nota bonus: 0.67

Exemplul 3: Vizite simulate cu distribuția Zipf

Din 10000 de vizite simulate, prima pagină primește cele mai multe, iar numărul scade repede spre ultimele pagini. Ultimul rând arată că un exponent s ≤ 1 dă nil.

package main

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

func main() {
    pagini := []string{"acasă", "php", "python", "go", "rust", "despre"}
    r := rand.New(rand.NewPCG(1, 2))
    z := rand.NewZipf(r, 1.5, 1, uint64(len(pagini)-1))

    vizite := make([]int, len(pagini))
    for range 10000 {
        vizite[z.Uint64()]++
    }
    for i, p := range pagini {
        fmt.Printf("%-7s %5d\n", p, vizite[i])
    }
    fmt.Println(rand.NewZipf(r, 0.5, 1, 10) == nil)
}

Rezultat

acasă    5447
php      1968
python   1034
go        709
rust      467
despre    375
true

Greșeli frecvente

  • Creezi generatorul cu un seed luat din timp (time.Now().UnixNano()) ca să fie „mai aleatoriu”: nu este nevoie, funcțiile de la nivelul pachetului sunt deja inițializate aleatoriu. Generatorul propriu are sens când vrei un seed fix.
  • Folosești același *rand.Rand din mai multe goroutine fără sincronizare: spre deosebire de funcțiile de la nivelul pachetului, un generator propriu nu este sigur pentru folosire concurentă.
  • Dai lui NewZipf un s ≤ 1 sau v < 1: primești nil, iar apelul z.Uint64() oprește programul cu panic.
  • Folosești NewChaCha8 cu un seed fix pentru chei sau parole: cu seed cunoscut, secvența poate fi reprodusă de oricine. Pentru secrete folosește crypto/rand.

Comenzi înrudite

  • math/rand/v2.IntN: Returnează un număr întreg aleatoriu din intervalul [0, n): IntN pentru int, plus variantele Int32N, Int64N, UintN, Uint32N, Uint64N.
  • math/rand/v2.Shuffle: Amestecă aleatoriu n elemente, apelând funcția swap pentru a interschimba elementele i și j.
  • math/rand/v2.Float64: Returnează un număr zecimal aleatoriu uniform din intervalul [0.0, 1.0): Float64 sau Float32.
  • math/rand/v2.Perm: Returnează o permutare aleatorie a numerelor 0, 1, …, n-1, ca slice nou de int.