यादृच्छिकता इतनी गंभीर चीज़ है कि इसे संयोग पर नहीं छोड़ा जा सकता
\n\nजिसने भी कभी Go में कोड लिखा है, उसने रैंडम नंबर जनरेशन का सामना किया है। रैंडम नंबर जनरेटर (RNG) एक धोखेबाज चीज़ है। देखने में सब कुछ सरल लगता है: rand.Intn() कॉल करें और एक नंबर मिल जाता है। लेकिन अगर आप सही सीडिंग (seed) पर ध्यान नहीं देते हैं, तो आपकी "यादृच्छिकता" एक पूर्वानुमानित अनुक्रम में बदल जाएगी, जो टेस्ट को तोड़ देगी, क्रिप्टोग्राफी में विफल हो जाएगी, या बस हर बार चलाने पर समान परिणाम देकर आपको आश्चर्यचकित कर देगी।
Stack Overflow पर "How to properly seed random number generator" प्रश्न ने हज़ारों बार देखा गया है। लोग time.Now().UnixNano() में भ्रमित हो जाते हैं, जहाँ ज़रूरत नहीं है वहाँ crypto/rand का उपयोग करते हैं, और कम प्रदर्शन से पीड़ित होते हैं। आज हम इस विषय को Go के उदाहरण के साथ समझेंगे - बिना पानी के, वास्तविक बेंचमार्क और तैयार समाधानों के साथ।
Seed सिर्फ एक औपचारिकता क्यों नहीं है?
\n\nSeed (बीज) जनरेटर की प्रारंभिक अवस्था है। एक ही बीज संख्याओं का एक ही अनुक्रम देता है। यह डिबगिंग के लिए सुविधाजनक है, लेकिन प्रोडक्शन के लिए घातक है यदि आप अप्रत्याशितता की उम्मीद करते हैं।
\n\nGo में, मानक पैकेज math/rand एक नियतात्मक एल्गोरिदम का उपयोग करता है। यदि आप rand.Seed() कॉल करना भूल जाते हैं, तो... Go 1.20 से पहले, डिफ़ॉल्ट जनरेटर एक निश्चित बीज (आमतौर पर 1) का उपयोग करता था। Go 1.20 से शुरू करके, math/rand प्रोग्राम शुरू होने पर स्वचालित रूप से एक यादृच्छिक मान से सीड हो जाता है। हालांकि, महत्वपूर्ण स्थानों पर इस पर निर्भर रहना एक बुरी प्रथा है।
शुरुआती लोगों की मुख्य समस्या प्रत्येक रैंडम नंबर के लिए जनरेटर को सीड करने का प्रयास करना है:
\n\n// ❌ खराब: हर बार जनरेटर को फिर से बनाना\nfunc getRandom() int {\n r := rand.New(rand.NewSource(time.Now().UnixNano()))\n return r.Intn(100)\n}\n\nयह खराब क्यों है? time.Now().UnixNano() नैनोसेकंड देता है, लेकिन वास्तव में सटीकता OS पर निर्भर करती है। Windows में, उदाहरण के लिए, यह ~15 ms हो सकती है। यदि आप फ़ंक्शन को लगातार दो बार कॉल करते हैं - तो आपको समान नंबर मिलेंगे। साथ ही, हर बार rand.NewSource कॉल करने से एक नया ऑब्जेक्ट बनता है, जो गार्बेज कलेक्टर पर लोड डालता है।
Go में सीडिंग के शीर्ष 3 तरीके
\n\n1. क्लासिक तरीका: एक बार ग्लोबल seed
\n\nअधिकांश एप्लिकेशन के लिए सबसे सरल और सही विकल्प प्रोग्राम शुरू होने पर एक बार seed सेट करना है।
\n\npackage main\n\nimport (\n "fmt"\n "math/rand"\n "time"\n)\n\nfunc main() {\n // ✅ अच्छा: एक बार सीड करें\n rand.Seed(time.Now().UnixNano())\n\n for i := 0; i < 5; i++ {\n fmt.Println(rand.Intn(100))\n }\n}\n\nमहत्वपूर्ण: time.Now().UnixNano() एक अद्वितीय मान देता है, लेकिन यदि प्रोग्राम एक ही नैनोसेकंड में कई बार चलता है (असंभावित, लेकिन सैद्धांतिक रूप से संभव), तो seed समान होगा। 99% एप्लिकेशन के लिए यह पर्याप्त है।
2. अलगाव के लिए लोकल जनरेटर
\n\nजब आपके पास कई goroutines हों या आप ग्लोबल स्थिति को प्रभावित नहीं करना चाहते हों, तो अपना स्वयं का rand.Rand इंस्टेंस बनाएं।
func NewSeededGenerator() *rand.Rand {\n seed := time.Now().UnixNano()\n return rand.New(rand.NewSource(seed))\n}\n\n// उपयोग:\nrng := NewSeededGenerator()\nfor i := 0; i < 3; i++ {\n fmt.Println(rng.Intn(100))\n}\n\nयह तरीका पूर्ण नियंत्रण देता है: अलग-अलग goroutines के अपने जनरेटर हो सकते हैं, और वे एक-दूसरे को ब्लॉक नहीं करते हैं (ग्लोबल rand म्यूटेक्स का उपयोग करता है)।
3. महत्वपूर्ण कार्यों के लिए क्रिप्टो-सुरक्षित seed
\n\nयदि आपको वास्तविक यादृच्छिकता चाहिए (टोकन, पासवर्ड, क्रिप्टोग्राफी) - तो math/rand को भूल जाएं। crypto/rand का उपयोग करें:
import (\n "crypto/rand"\n "encoding/binary"\n "math/rand"\n)\n\nfunc CryptoSeed() int64 {\n var buf [8]byte\n _, err := rand.Read(buf[:])\n if err != nil {\n