Tarkibga o'tish

Go’da tasodifiy qiymatlar yaratish

Tasodifiy qiymatlar o‘yin, simulyatsiya, test ma’lumoti, elementlarni aralashtirish va xavfsizlik tokenlarini yaratishda ishlatiladi. Lekin barcha tasodifiy generatorlar bir xil vazifa uchun mo‘ljallanmagan.

Go’da ikki asosiy paket bor:

  • math/rand — tez, psevdotasodifiy qiymatlar uchun;
  • crypto/rand — taxmin qilish qiyin bo‘lgan, kriptografik xavfsiz qiymatlar uchun.

Ularning nomi bir xil bo‘lsa ham, xavfsizlik xususiyatlari boshqa. Session ID, parolni tiklash tokeni yoki API kalitini math/rand bilan yaratish xavfli.

Psevdotasodifiy son nima?

Psevdotasodifiy sonlar generatori (PRNG) boshlang‘ich holat va algoritm asosida sonlar ketma-ketligini hisoblaydi. Natijalar tasodifiy ko‘rinadi, ammo bir xil boshlang‘ich qiymat (seed) bir xil generatorga berilsa, ketma-ketlik takrorlanadi.

Bu xususiyat test va simulyatsiyada foydali. Xato faqat muayyan ketma-ketlikda yuz bersa, o‘sha seed bilan holatni qayta yaratish mumkin. Hujumchi taxmin qilmasligi kerak bo‘lgan qiymatlar uchun esa bu xususiyat mos emas.

math/rand bilan ishlash

math/randning paket darajasidagi funksiyalari avtomatik boshlang‘ich holatga ega. Zamonaviy Go versiyalarida dasturni har ishga tushirganda qo‘lda time.Now().UnixNano() bilan global seed o‘rnatish kerak emas.

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    n := rand.Intn(100)
    fmt.Println("Tasodifiy son:", n)
}

Mumkin bo‘lgan natija:

Tasodifiy son: 57

rand.Intn(100) [0, 100) yarim ochiq oraliqdan qiymat qaytaradi. Demak, eng kichik qiymat 0, eng kattasi 99. Natija har ishga tushirishda farq qilishi mumkin.

Warning

rand.Intn(n) uchun n musbat bo‘lishi shart. n <= 0 bo‘lsa, dastur panic qiladi. Tashqaridan kelgan chegarani avval tekshiring.

Ixtiyoriy oraliqdan son olish

mindan maxgacha, ikkala chegara ham kiradigan son kerak bo‘lsa, oraliq kengligi max-min+1 bo‘ladi:

package main

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

func randomBetween(min, max int) (int, error) {
    if min > max {
        return 0, errors.New("min maxdan katta bo‘lmasligi kerak")
    }
    return min + rand.Intn(max-min+1), nil
}

func main() {
    n, err := randomBetween(10, 15)
    if err != nil {
        fmt.Println("Xato:", err)
        return
    }
    fmt.Println(n)
}

Mumkin bo‘lgan natija:

13

Funksiya 10, 11, 12, 13, 14 yoki 15 qiymatlaridan birini qaytaradi. min > max holati tekshirilgani uchun Intn()ga noto‘g‘ri argument uzatilmaydi.

Bu formulada ekstremal int qiymatlari ishlatilsa, max-min+1 overflow qilishi mumkin. Oddiy kichik diapazonlarda muammo yo‘q. Ishonchsiz tashqi qiymatlar yoki juda katta chegaralar bilan ishlaganda ayirishdan oldin diapazonni cheklash kerak.

Takrorlanuvchi natija uchun lokal generator

Testda bir xil ketma-ketlik kerak bo‘lsa, global rand.Seed() o‘rniga alohida generator yarating:

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    first := rand.New(rand.NewSource(42))
    second := rand.New(rand.NewSource(42))

    for i := 0; i < 3; i++ {
        fmt.Println(first.Intn(100) == second.Intn(100))
    }
}

Natija:

true
true
true

rand.NewSource(42) bir xil boshlang‘ich holatli ikkita manba yaratadi. Har bir generatorga metodlar bir xil tartibda chaqirilgani uchun juft natijalar teng chiqadi. Bitta generatorga qo‘shimcha chaqiruv qilinsa, ularning holati turlicha bo‘lib qoladi.

Paket darajasidagi rand.Seed() eskirgan. Joriy Go versiyalarida u global generatorni boshqarish uchun ishlatilmasligi kerak. Deterministik oqim zarur bo‘lsa, rand.New(rand.NewSource(seed)) aniqroq va boshqa kodga ta’sir qilmaydi.

Warning

rand.NewSource() qaytargan manba va undan yaratilgan *rand.Rand bir nechta goroutine tomonidan bir vaqtda ishlatish uchun xavfsiz emas. Har bir goroutine uchun alohida generator ishlating yoki murojaatni mutex bilan himoyalang. Paket darajasidagi funksiyalar esa concurrent ishlatishga mos.

Elementlarni aralashtirish

rand.Shuffle() slice elementlarining tartibini o‘zgartiradi:

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    names := []string{"Ali", "Vali", "Aziza", "Madina"}

    rand.Shuffle(len(names), func(i, j int) {
        names[i], names[j] = names[j], names[i]
    })

    fmt.Println(names)
}

Mumkin bo‘lgan natija:

[Aziza Ali Madina Vali]

Callback i va j indekslaridagi elementlarni almashtiradi. Slice’ning o‘zi o‘zgaradi, nusxa yaratilmaydi. Bu test ma’lumotlarini aralashtirish yoki o‘yin navbatini belgilashda foydali. Xavfsizlikka oid tasodifiy tartib uchun math/rand yetarli emas.

crypto/rand bilan xavfsiz son olish

crypto/rand operatsion tizim taqdim etgan kriptografik tasodifiylik manbasidan foydalanadi. Unga foydalanuvchi seed bermaydi.

package main

import (
    "crypto/rand"
    "fmt"
    "math/big"
)

func main() {
    max := big.NewInt(101)
    n, err := rand.Int(rand.Reader, max)
    if err != nil {
        fmt.Println("Xato:", err)
        return
    }

    fmt.Println("Tasodifiy son:", n)
}

Mumkin bo‘lgan natija:

Tasodifiy son: 16

rand.Int() [0, max) oraliqdan bir xil ehtimolli *big.Int qaytaradi. big.NewInt(101) sabab natija 0dan 100gacha bo‘ladi. max <= 0 bo‘lsa, funksiya panic qiladi. Shu sababli dinamik chegarani oldindan tekshirish kerak.

Paketlar nomi to‘qnashsa, alias ishlatish mumkin:

import (
    cryptorand "crypto/rand"
    mathrand "math/rand"
)

Shundan keyin kodda cryptorand.Int() va mathrand.Intn() yoziladi. Bu parcha import nomlarini ajratish usulini ko‘rsatadi; alohida dastur emas.

Xavfsiz token yaratish

Token uchun tasodifiy baytlarni matnga kodlash keng tarqalgan usul:

package main

import (
    "crypto/rand"
    "encoding/hex"
    "fmt"
)

func main() {
    data := make([]byte, 16)
    if _, err := rand.Read(data); err != nil {
        fmt.Println("Xato:", err)
        return
    }

    token := hex.EncodeToString(data)
    fmt.Println("Token uzunligi:", len(token))
}

Natija:

Token uzunligi: 32

16 tasodifiy bayt 128 bit tasodifiylik beradi. Har bir bayt hexadecimal ko‘rinishda ikkita belgi bilan yozilgani uchun token uzunligi doim 32 belgi bo‘ladi. Tokenning o‘zi har safar o‘zgaradi.

Yangi Go versiyalarida crypto/rand.Read() berilgan slice’ni to‘liq to‘ldiradi. Xato holatini tekshiradigan kod eski Go versiyalari bilan ham mos va chaqiruvning xavfsizlikka oid ekanini aniq ko‘rsatadi.

Modulo bias nima?

Tasodifiy baytni % n orqali kichik oraliqqa keltirish har doim bir xil taqsimot bermaydi. Masalan, bayt 0..255 oralig‘ida, n esa 10 bo‘lsa, ayrim qoldiqlar boshqalaridan ko‘proq uchraydi. Bu modulo og‘ishi (modulo bias) deyiladi.

// Kriptografik kod uchun noto‘g‘ri yondashuv:
digit := randomByte % 10

crypto/rand.Int() mos kelmaydigan qiymatlarni tashlab, bir xil taqsimotni saqlaydi. Tasdiqlash kodi yoki xavfsiz indeks tanlashda undan foydalaning.

Qaysi paketni tanlash kerak?

Vazifa Paket Sabab
Testni takrorlash math/rand lokal generatori Bir xil seed bir xil oqim beradi
Simulyatsiya yoki o‘yin math/rand Tez va qulay
Slice’ni xavfsizlikka aloqasiz aralashtirish math/rand Shuffle() mavjud
Session ID yoki reset token crypto/rand Natijani taxmin qilish qiyin
Kriptografik kalit crypto/rand Xavfsiz tasodifiy baytlar beradi
Bir martalik tasdiqlash kodi crypto/rand Bir xil taqsimot va taxmin qilishga chidamlilik kerak

Keng tarqalgan xatolar

  • math/randni xavfsizlik tokeni uchun ishlatish. Seed yoki generator holati aniqlansa, keyingi qiymatlar taxmin qilinishi mumkin.
  • Har chaqiruv oldidan yangi vaqt seed’i yaratish. Tez ketma-ket chaqiruvlar bir xil yoki bog‘liq seed olishi mumkin. Bitta generatorni qayta ishlating.
  • Intn() yuqori chegarani ham qo‘shadi deb o‘ylash. Intn(10) faqat 0..9 qiymatlarini beradi.
  • n <= 0 holatini tekshirmaslik. Intn() va crypto/rand.Int() noto‘g‘ri chegara bilan panic qiladi.
  • Tasodifiy natijaga aniq test yozish. 0 <= n && n < max kabi invariantni tekshiring yoki lokal generatorga belgilangan seed bering.

Interviewda nimalarga e’tibor beriladi?

  • Psevdotasodifiy va kriptografik xavfsiz generator o‘rtasidagi farq.
  • Yarim ochiq [0, n) oraliq va Intn(n)ning panic sharti.
  • Takrorlanuvchi test uchun global rand.Seed() emas, lokal *rand.Rand ishlatish.
  • Lokal rand.Sourcening concurrent foydalanishga xavfsiz emasligi.
  • % n bilan diapazonga keltirishdagi modulo bias.

Xulosa

math/rand simulyatsiya, o‘yin va takrorlanuvchi testlar uchun ishlatiladi. crypto/rand esa token, kalit va tasdiqlash kodi kabi xavfsizlikka oid qiymatlar uchun kerak. Chegaralarni tekshiring, [0, n) oraliqni hisobga oling va kriptografik qiymatlarni % n bilan yasamang.