कैसे, सफलतापूर्वक, और अच्छी तरह से mt19937 PRNG बीज?


112

मुझे ऐसे कई उत्तर दिखाई देते हैं जिनमें कोई व्यक्ति <random>यादृच्छिक संख्याओं का उपयोग करने का सुझाव देता है , आमतौर पर इस तरह कोड के साथ:

std::random_device rd;  
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);

आमतौर पर यह किसी प्रकार की "अपवित्र घृणा" को प्रतिस्थापित करता है जैसे:

srand(time(NULL));
rand()%6;

हम पुराने तरीके से यह तर्क देकर आलोचना कर सकते हैं कि time(NULL)कम एन्ट्रापी प्रदान करता है, time(NULL)अनुमानित है, और अंतिम परिणाम गैर-समान है।

लेकिन यह सब नए तरीके से सच है: इसमें सिर्फ शिनियर लिबास है।

  • rd()सिंगल देता है unsigned int। इसमें कम से कम 16 बिट्स और शायद 32 हैं। यह एमटी के 19937 बिट्स स्टेट को सीड करने के लिए पर्याप्त नहीं है।

  • std::mt19937 gen(rd());gen()32 बिट्स के साथ (पहली आउटपुट को देखते हुए) का उपयोग करना एक अच्छा आउटपुट वितरण नहीं देता है। 7 और 13 कभी भी पहला आउटपुट नहीं हो सकता है। दो बीजों का उत्पादन 0. 12 बीजों का उत्पादन 1226181350 होता है। ( लिंक )

  • std::random_deviceहो सकता है, और कभी-कभी एक निश्चित बीज के साथ एक साधारण PRNG के रूप में लागू किया जाता है। इसलिए यह प्रत्येक रन पर एक ही अनुक्रम का उत्पादन कर सकता है। ( लिंक ) यह इससे भी बदतर है time(NULL)

इससे भी बुरी बात यह है कि, पूर्वगामी कोड स्निपेट को कॉपी और पेस्ट करना बहुत आसान है, इसके बावजूद कि वे समस्याएं हैं। इसके कुछ समाधानों के लिए लार्गिश पुस्तकालयों का अधिग्रहण करने की आवश्यकता होती है जो हर किसी के लिए उपयुक्त नहीं हो सकते हैं।

इस के प्रकाश में, मेरा सवाल यह है कि C ++ में mt19937 PRNG को कैसे एकांत में, आंशिक रूप से और अच्छी तरह से बीज दिया जा सकता है?

उपरोक्त मुद्दों को देखते हुए, एक अच्छा जवाब:

  • Mt19937 / mt19937_64 को पूरी तरह से बीज देना चाहिए।
  • केवल std::random_deviceया time(NULL)एंट्रोपी के स्रोत के रूप में भरोसा नहीं कर सकता ।
  • बूस्ट या अन्य परिवादियों पर भरोसा नहीं करना चाहिए।
  • कम संख्या में ऐसे फिट होना चाहिए कि यह उत्तर में अच्छी कॉपी-पेस्ट लगे।

विचार

  • मेरा वर्तमान विचार है कि आउटपुट से std::random_device(शायद XOR के माध्यम से) मैश किया जा सकता है time(NULL), एड्रेस स्पेस रैंडमाइजेशन से प्राप्त मान , और एक कठिन-कोडित स्थिरांक (जो वितरण के दौरान सेट किया जा सकता है) एंट्रोपी में सबसे अच्छा प्रयास प्राप्त करने के लिए।

  • std::random_device::entropy() एक अच्छा संकेत नहीं देता है कि क्या कर std::random_deviceसकता है या नहीं।


24
@ फैबियन: इसके बारे में क्या पोर्टेबल है? यह एक C ++ प्रश्न है, न कि लिनक्स प्रश्न।
ऑर्बिट

6
मेरा व्यक्तिगत विचार आने लगे कि शायद मूल्यों से खींचा जा सकता था std::random_device, time(NULL),, और समारोह पतों फिर एक साथ XORed सबसे अच्छा प्रयास एन्ट्रापी स्रोत का एक प्रकार का निर्माण करने के।
रिचर्ड

5
यह अच्छा होगा यदि कोई कार्य करता है जैसे कि do_random_device_actually_work () तो कोई कम से कम इनायत से नीचा दिखा सकता है, या उपयोगकर्ता के लिए चेतावनी या त्रुटियों का उत्पादन कर सकता है।

4
उचित समाधान छोटा नहीं है, छोटा समाधान उचित नहीं होगा। मेरा दृष्टिकोण मैं अपने सीड 11 लाइब्रेरी में उपयोग करता हूं , मूल रूप से std::random_deviceउन प्लेटफार्मों पर ठीक से लागू करने के लिए जिन्हें आप अपना कार्यक्रम चलाने की योजना बना रहे हैं, और एक सहायक फ़ंक्शन प्रदान करते हैं जो एक वरीयता प्राप्त जनरेटर ( seed11::make_seeded<std::mt19937>())
सहस्राब्दी

5
एक तरफ: आपकी दूसरी गोली कुछ नया नहीं जोड़ती है। यह आश्चर्य की बात नहीं है कि आपको कुछ मूल्य मिला जो 12 बार दिखाई देता है। आपको यह अपेक्षा करनी चाहिए कि तीन मान केवल 12 बार दिखाई दें , यह मानते हुए कि आपके पास 2 ^ 32 स्वतंत्र, समान रूप से यादृच्छिक नमूने हैं।

जवाबों:


58

मैं इसके साथ सबसे बड़ी खामी का तर्क दूंगा std::random_deviceकि अगर कोई CSPRNG उपलब्ध नहीं है, तो यह एक निर्धारक गिरावट की अनुमति है। यह अकेले एक PRNG का उपयोग न करने का एक अच्छा कारण है std::random_device, क्योंकि उत्पादित बाइट्स निर्धारक हो सकती हैं। दुर्भाग्यवश ऐसा होने पर, या निम्न-गुणवत्ता वाले यादृच्छिक संख्याओं के बजाय विफलता का अनुरोध करने के लिए एक एपीआई प्रदान नहीं करता है।

यही है, कोई पूरी तरह से पोर्टेबल समाधान नहीं है: हालांकि, एक सभ्य, न्यूनतम दृष्टिकोण है। sysrandomPRNG को बोने के लिए आप CSPRNG ( नीचे के रूप में परिभाषित ) के आसपास एक न्यूनतम आवरण का उपयोग कर सकते हैं ।

खिड़कियाँ


आप CryptGenRandomएक CSPRNG पर भरोसा कर सकते हैं । उदाहरण के लिए, आप निम्नलिखित कोड का उपयोग कर सकते हैं:

bool acquire_context(HCRYPTPROV *ctx)
{
    if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
        return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
    }
    return true;
}


size_t sysrandom(void* dst, size_t dstlen)
{
    HCRYPTPROV ctx;
    if (!acquire_context(&ctx)) {
        throw std::runtime_error("Unable to initialize Win32 crypt library.");
    }

    BYTE* buffer = reinterpret_cast<BYTE*>(dst);
    if(!CryptGenRandom(ctx, dstlen, buffer)) {
        throw std::runtime_error("Unable to generate random bytes.");
    }

    if (!CryptReleaseContext(ctx, 0)) {
        throw std::runtime_error("Unable to release Win32 crypt library.");
    }

    return dstlen;
}

यूनिक्स की तरह


कई यूनिक्स जैसी प्रणालियों पर, आपको जब संभव हो तो / dev / urandom का उपयोग करना चाहिए (हालाँकि यह POSIX-compliant सिस्टम पर मौजूद होने की गारंटी नहीं है)।

size_t sysrandom(void* dst, size_t dstlen)
{
    char* buffer = reinterpret_cast<char*>(dst);
    std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
    stream.read(buffer, dstlen);

    return dstlen;
}

अन्य


यदि कोई CSPRNG उपलब्ध नहीं है, तो आप भरोसा करना चुन सकते हैं std::random_device। हालाँकि, यदि संभव हो तो मैं इससे बचता हूँ, क्योंकि विभिन्न संकलक (सबसे विशेष रूप से, MinGW) इसे PRNG के रूप में कार्यान्वित करते हैं (वास्तव में, मनुष्यों को सचेत करने के लिए हर बार उसी क्रम का निर्माण करना कि यह ठीक से यादृच्छिक नहीं है)।

बोने


अब हमारे पास न्यूनतम ओवरहेड के साथ हमारे टुकड़े हैं, हम अपने PRNG को सीड करने के लिए यादृच्छिक एंट्रोपी के वांछित बिट्स उत्पन्न कर सकते हैं। PRNG को बोने के लिए उदाहरण (स्पष्ट रूप से अपर्याप्त) 32-बिट्स का उपयोग करता है, और आपको इस मान को बढ़ाना चाहिए (जो आपके CSPRNG पर निर्भर है)।

std::uint_least32_t seed;    
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);

बूस्ट से तुलना


हम स्रोत कोड पर त्वरित नज़र डालने के बाद :: random_device (एक सच्चा CSPRNG) को बढ़ावा देने के लिए समानताएं देख सकते हैं । बूस्ट MS_DEF_PROVविंडोज पर उपयोग करता है, जिसके लिए प्रदाता प्रकार है PROV_RSA_FULL। केवल एक चीज गायब है जो क्रिप्टोग्राफिक संदर्भ की पुष्टि करेगी, जिसके साथ किया जा सकता है CRYPT_VERIFYCONTEXT। * निक्स पर, बूस्ट का उपयोग करता है /dev/urandom। IE, यह समाधान पोर्टेबल, अच्छी तरह से परीक्षण किया गया है, और उपयोग में आसान है।

लिनक्स विशेषज्ञता


यदि आप सुरक्षा के लिए getrandomपर्याप्तता का त्याग करने के लिए तैयार हैं, तो लिनक्स 3.17 और इसके बाद के संस्करण और हाल ही के सोलारिस पर एक उत्कृष्ट विकल्प है। यह पता getrandomलगाने के लिए /dev/urandomकि क्या कर्नेल ने अपने CSPRNG को बूट करने के बाद अभी तक इनिशियलाइज़ नहीं किया है, को छोड़कर, यह व्यवहारिक रूप से व्यवहार करता है । यदि लिनक्स getrandomउपलब्ध है, तो निम्नलिखित स्निपेट पता लगाता है और यदि वापस नहीं आता है /dev/urandom

#if defined(__linux__) || defined(linux) || defined(__linux)
#   // Check the kernel version. `getrandom` is only Linux 3.17 and above.
#   include <linux/version.h>
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
#       define HAVE_GETRANDOM
#   endif
#endif

// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
#   include <sys/syscall.h>
#   include <linux/random.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#elif defined(_WIN32)

// Windows sysrandom here.

#else

// POSIX sysrandom here.

#endif

OpenBSD


एक अंतिम चेतावनी है: आधुनिक ओपनबीएसडी में नहीं है /dev/urandom। आपको इसके बजाय गेटेंट्रोपी का उपयोग करना चाहिए ।

#if defined(__OpenBSD__)
#   define HAVE_GETENTROPY
#endif

#if defined(HAVE_GETENTROPY)
#   include <unistd.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = getentropy(dst, dstlen);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#endif

अन्य विचार


यदि आपको क्रिप्टोग्राफिक रूप से सुरक्षित रैंडम बाइट्स की आवश्यकता है, तो आपको संभवतः पॉस्क्स के अनबर्डेड ओपन / रीड / क्लोज़ के साथ फ़्लोस्ट को बदलना चाहिए। इसका कारण यह है दोनों है basic_filebufऔर FILEएक आंतरिक बफर है, जो एक मानक संभाजक के माध्यम से आवंटित किया जाएगा (और इसलिए स्मृति से मिटा दिया नहीं) होते हैं।

इसे आसानी से बदलकर किया जा सकता है sysrandom:

size_t sysrandom(void* dst, size_t dstlen)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        throw std::runtime_error("Unable to open /dev/urandom.");
    }
    if (read(fd, dst, dstlen) != dstlen) {
        close(fd);
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    close(fd);
    return dstlen;
}

धन्यवाद


इंगित करने के लिए बेन वोइग्ट के लिए विशेष धन्यवाद FILEबफ़र्ड रीड का उपयोग करता है, और इसलिए इसका उपयोग नहीं किया जाना चाहिए।

मैं पीटर कॉर्ड्स का उल्लेख करने के लिए धन्यवाद देना चाहूंगा getrandom, और ओपनबीएसडी की कमी /dev/urandom


11
यह वही है जो मैंने अतीत में किया है, लेकिन कम से कम एक सवाल है, डब्ल्यूटीएफ इन प्लेटफार्मों के लिए पुस्तकालय लेखकों को हमारे लिए ऐसा नहीं कर सकता है? मुझे उम्मीद है कि फ़ाइल का उपयोग और धागे (उदाहरण के लिए) पुस्तकालय कार्यान्वयन द्वारा सारगर्भित होने के लिए होंगे, इसलिए यादृच्छिक संख्या पीढ़ी क्यों नहीं?

2
ओपी यहाँ: यह अच्छा होगा यदि इस उत्तर ने बीजारोपण को थोड़ा बेहतर प्रदर्शित किया। जितना संभव हो, मैं उन उत्तरों की उम्मीद कर रहा हूं जो कॉपी-पेस्ट करने योग्य कोड उत्पन्न करते हैं जो कि मेरे प्रश्न में पोस्ट किए गए सरल उदाहरण की तुलना में बेहतर काम करता है जो कोडर की ओर से बहुत तकनीकी व्याख्या या विचार के बिना आवश्यक है।
रिचर्ड

4
मैंने सोचा कि /dev/randomएक आरएनजी को बोने के लिए बेहतर विकल्प होगा, लेकिन जाहिरा तौर /dev/urandomपर अभी भी कम्प्यूटेशनल रूप से सुरक्षित माना जाता है , जब /dev/randomकम उपलब्ध एन्ट्रॉपी के कारण भी अवरुद्ध हो जाएगा, इसलिए urandomशायद एक बार के पैड को छोड़कर सब कुछ के लिए अनुशंसित विकल्प है। यह भी देखें unix.stackexchange.com/questions/324209/...urandomहालांकि, बूटअप के बाद बहुत पहले से पूर्वानुमानित बीजों से सावधान रहें ।
पीटर कॉर्डेस

2
लिनक्स की getrandom(2)प्रणाली कॉल खोलने और पढ़ने की तरह है /dev/urandom, सिवाय इसके कि अगर कर्नेल के यादृच्छिकता स्रोतों को अभी तक आरंभ नहीं किया गया है तो यह ब्लॉक हो जाएगा। मुझे लगता है कि यह आपको अन्य मामलों में ब्लॉक किए बिना शुरुआती-बूट कम-गुणवत्ता-यादृच्छिकता समस्या से बचाता /dev/randomहै।
पीटर कॉर्ड्स

1
@PeterCordes, निश्चित रूप से, और यह उपलब्ध होने पर एक बढ़िया विकल्प है। हालांकि, यह बीएसडी या अन्य * निक्स पर काम नहीं करता है, जो कि /dev/urandomआमतौर पर काम करता है। इस पर पायथन मेलिंग सूची चर्चा कुछ ऐसी है जो मैं आमतौर पर सदस्यता लेता हूं: bugs.python.org/issue27266
अलेक्जेंडर हज़ागघ

22

एक अर्थ में, यह कुछ भी नहीं किया जा सकता है। यही है, एक C + + (एक सिम्युलेटर, जो मशीन घड़ी को नियत रूप से निर्धारित करता है, और एक "निर्धारक" I / O) के साथ चलने वाले एक वैध पूर्ण-नियतात्मक मंच को गर्भ धारण कर सकता है जिसमें PRNG बीजने के लिए यादृच्छिकता का कोई स्रोत नहीं है।


1
@kbelder: 1. कौन कहता है कि उपयोगकर्ता एक व्यक्ति है? 2. सभी कार्यक्रमों में उपयोगकर्ता की सहभागिता नहीं होती है और आप निश्चित रूप से यह मान ही नहीं सकते हैं कि हमेशा एक उपयोगकर्ता आसपास होता है ...
einpoklum

8
मैं इस प्रतिक्रिया की सराहना करता हूं, लेकिन साथ ही यह भी महसूस करता हूं कि एक कार्यक्रम को एक उचित सर्वोत्तम प्रयास करना चाहिए।
रिचर्ड

3
@ रीचर्ड सहमत हैं, लेकिन मुद्दा यह है कि C ++ मानक लेखकों के पास इन प्रकार के विचित्र स्थितियों को समायोजित करने के लिए (या कम से कम अपने प्रियतम की कोशिश) है। इसीलिए आपको इस प्रकार की हार्दिक-वाश मानक परिभाषाएँ प्राप्त होती हैं, जहाँ आपको अच्छे परिणाम मिल सकते हैं, लेकिन कंपाइलर अभी भी मानक-अनुपालन हो सकता है, भले ही यह कुछ ऐसा वापस दे जो कार्यात्मक रूप से बेकार हो। - तो आपके प्रतिबंध ("अन्य पर भरोसा नहीं कर सकते हैं") किसी भी प्रतिक्रिया से इनकार करते हैं, क्योंकि आपको प्रभावी रूप से एक प्लेटफॉर्म-बाय-प्लेटफॉर्म / कंपाइलर-बाय-कंपाइलर विशेष आवरण की आवश्यकता होती है। (उदा। बूस्ट इतनी अच्छी तरह से क्या करते हैं।)
आरएम

2
@ रिचर्ड, जो यह बताता है, हालांकि, यह है कि आपको वही मिलता है जो आपको मानक में मिलता है क्योंकि बेहतर करने का कोई पोर्टेबल तरीका नहीं है। यदि आप बेहतर करना चाहते हैं (जो एक महान लक्ष्य है) तो आपको कुछ अधिक या कम मात्रा में घृणा को स्वीकार करना होगा :)
हॉब्स जुब

1
@ रीचर्ड: कभी-कभी आपको बस यह स्वीकार करना होगा कि मानकों का अनुपालन करना संभव है C ++ कार्यान्वयन जो उपयोगी नहीं है। चूंकि कार्यान्वयन लोग किसी भी चीज़ के लिए उपयोग करते हैं जो मामलों को उपयोगी बनाने के लिए डिज़ाइन किया गया है, आपको कभी-कभी "किसी भी कार्यान्वयन को कुछ उचित करना होगा" जैसे तर्कों के साथ रहना होगा। मुझे उम्मीद है कि std::random_deviceयह उस श्रेणी में होगा, लेकिन स्पष्ट रूप से ऐसा नहीं है कि कुछ वास्तविक कार्यान्वयन एक निश्चित-बीज PRNG का उपयोग करते हैं! यह einpoklum के तर्क से परे है।
पीटर कॉर्ड्स

14

आप एक का उपयोग कर सकते हैं std::seed_seqऔर इसे कम से कम अलेक्जेंडर हुज़ाग के एंट्रॉपी प्राप्त करने की विधि का उपयोग करके जनरेटर के लिए राज्य के आकार की आवश्यकता के लिए भर सकते हैं :

size_t sysrandom(void* dst, size_t dstlen); //from Alexander Huszagh answer above

void foo(){

    std::array<std::mt19937::UIntType, std::mt19937::state_size> state;
    sysrandom(state.begin(), state.length*sizeof(std::mt19937::UIntType));
    std::seed_seq s(state.begin(), state.end());

    std::mt19937 g;
    g.seed(s);
}

अगर वहाँ एक उचित तरीके से भरने या एक का निर्माण करना था SeedSequence एक से UniformRandomBitGenerator मानक पुस्तकालय का उपयोग करने में std::random_deviceठीक से बोने बहुत सरल हो जाएगा के लिए।


1
seed_seq के पास समस्याएँ हैं हालांकि, pcg-random.org/posts/developing-a-seed_seq-alternative.html
etarion

जब आप बीज_सेक से बीज लेते हैं तो रेंडम संख्या जनरेटर पूरे सरणी का उपयोग करने की गारंटी देने के लिए सी ++ मानक या किसी भी चीज में कुछ भी नहीं है। यह विधि विफलता का कारण बन जाएगी यदि आप वैज्ञानिक सिमुलेशन के लिए आरंग का उपयोग कर रहे हैं, और जाहिर है क्रिप्टोग्राफी के लिए भी। इसके लिए केवल उपयोग के मामले के बारे में एक वीडियोगेम बेतरतीब करना होगा, लेकिन वहां यह एक ओवरकिल होगा।
कोस्टास

5

मैं जिस कार्यान्वयन पर काम कर रहा हूं state_sizeवह mt19937PRNG की संपत्ति का लाभ उठाकर यह तय करता है कि आरंभीकरण पर कितने बीज प्रदान करने हैं:

using Generator = std::mt19937;

inline
auto const& random_data()
{
    thread_local static std::array<typename Generator::result_type, Generator::state_size> data;
    thread_local static std::random_device rd;

    std::generate(std::begin(data), std::end(data), std::ref(rd));

    return data;
}

inline
Generator& random_generator()
{
    auto const& data = random_data();

    thread_local static std::seed_seq seeds(std::begin(data), std::end(data));
    thread_local static Generator gen{seeds};

    return gen;
}

template<typename Number>
Number random_number(Number from, Number to)
{
    using Distribution = typename std::conditional
    <
        std::is_integral<Number>::value,
        std::uniform_int_distribution<Number>,
        std::uniform_real_distribution<Number>
    >::type;

    thread_local static Distribution dist;

    return dist(random_generator(), typename Distribution::param_type{from, to});
}

मुझे लगता है कि इसमें सुधार की गुंजाइश है क्योंकि आकार और सीमा में std::random_device::result_typeअंतर हो सकता है std::mt19937::result_typeइसलिए इसे वास्तव में ध्यान में रखा जाना चाहिए।

Std के बारे में एक नोट :: random_device

C++11(/14/17)मानक के अनुसार :

२६.५.६ कक्षा random_device [ rand.device ]

2 यदि कार्यान्वयन सीमाएँ गैर-नियतात्मक यादृच्छिक संख्याओं को उत्पन्न करने से रोकती हैं, तो कार्यान्वयन यादृच्छिक संख्या इंजन को नियोजित कर सकता है।

इसका मतलब है कि कार्यान्वयन केवल नियतात्मक मूल्यों को उत्पन्न कर सकता है यदि इसे कुछ सीमा द्वारा गैर-नियतात्मक लोगों को उत्पन्न करने से रोका जाता है ।

MinGWपर संकलक Windowsप्रसिद्धि से प्रदान नहीं करता है गैर नियतात्मक अपने से मूल्यों std::random_device, के बावजूद उन्हें ऑपरेटिंग सिस्टम से आसानी से उपलब्ध है। इसलिए मैं इसे एक बग मानता हूं और कार्यान्वयन और प्लेटफार्मों पर एक सामान्य घटना होने की संभावना नहीं है।


1
यह MT राज्य को भर सकता है, लेकिन फिर भी पूरी तरह से निर्भर करता है std::random_device, और इसलिए इससे उपजी समस्याओं के प्रति संवेदनशील है।
रिचर्ड

1
मुझे लगता है कि मैंने उन्हें प्रश्न में स्पष्ट रूप से पर्याप्त बताया। हालांकि स्पष्ट / चर्चा में खुशी हुई।
रिचर्ड

2
@ रिचर्ड क्या कोई वास्तविक प्रणाली है जो वास्तव में एक उचित लागू नहीं करते हैं std::random_device? मुझे पता है कि मानक एक PRNGवापस गिरने की अनुमति देता है, लेकिन मुझे लगता है कि यह सिर्फ खुद को कवर करने के लिए है क्योंकि यह मांग करना कठिन है कि उपयोग करने वाले प्रत्येक उपकरण C++में एक गैर-नियतात्मक यादृच्छिक स्रोत है। और अगर वे नहीं करते हैं तो आप उस बारे में क्या कर सकते हैं?
गैलिक

5
@AlexanderHuszagh मैं इतना निश्चित नहीं हूं। मेरा इरादा डिवाइस पर निर्भर मेरा "पोर्टेबल समाधान" बनाना है क्योंकि अगर डिवाइस गैर-नियतात्मक जनरेटर का समर्थन करता है तो ऐसा करना चाहिए std::random_device। मेरा मानना ​​है कि यह मानक की भावना है। इसलिए मैंने खोज की है और केवल वही पा सकता हूं जो MinGWइस संबंध में टूट गया है। कोई भी इस समस्या के बारे में किसी और चीज़ के साथ रिपोर्ट नहीं करता है जो मुझे मिली है। इसलिए, मेरी लाइब्रेरी में, मैंने बस MinGWअसमर्थित के रूप में चिह्नित किया है। अगर कोई व्यापक समस्या थी तो मैं इसे फिर से सोचूंगा। मैं अभी उस सबूत को नहीं देखता हूं।
गैलिक

5
मैं वास्तव में निराश हूं कि मिनगॉव std::random_deviceहर किसी के लिए इसे एक ऐसे रूप में उपलब्ध करा रहा है जो मंच की यादृच्छिकता क्षमताओं को वितरित नहीं करता है। निम्न गुणवत्ता वाले कार्यान्वयन मौजूदा API के उद्देश्य को पराजित करते हैं। यह बेहतर होगा IMO अगर वे इसे लागू नहीं किया जब तक वे यह काम कर रहे हैं। (या बेहतर है, अगर एपीआई उच्च गुणवत्ता वाले यादृच्छिकता उपलब्ध नहीं होने पर विफलता का अनुरोध करने का एक तरीका प्रदान करता है, तो मिनगीड सुरक्षा जोखिम पैदा करने से बच सकता है जबकि अभी भी गेम या जो भी हो, के लिए अलग-अलग बीज दे रहा है।)
पीटर कॉर्ड्स

2

समय का उपयोग करके बोने में कुछ भी गलत नहीं है, यह मानते हुए कि आपको इसे सुरक्षित करने की आवश्यकता नहीं है (और आपने यह नहीं कहा कि यह आवश्यक था)। अंतर्दृष्टि यह है कि आप गैर-यादृच्छिकता को ठीक करने के लिए हैशिंग का उपयोग कर सकते हैं। मैंने पाया है कि यह सभी मामलों में पर्याप्त रूप से काम करता है, जिसमें शामिल हैं और भारी मोंटे कार्लो सिमुलेशन के लिए विशेष रूप से।

इस दृष्टिकोण की एक अच्छी विशेषता यह है कि यह अन्य बीजों के यादृच्छिक-यादृच्छिक सेटों से आरंभ करने के लिए सामान्यीकरण करता है। उदाहरण के लिए, यदि आप चाहते हैं कि प्रत्येक थ्रेड का अपना RNG (थ्रेडसेफ़्टी के लिए) हो, तो आप केवल हैशेड थ्रेड आईडी के आधार पर आरंभ कर सकते हैं।

निम्नलिखित एक SSCCE है , जो मेरे कोडबेस से सरल है (सादगी के लिए; कुछ OO समर्थन संरचनाएं हैं):

#include <cstdint> //`uint32_t`
#include <functional> //`std::hash`
#include <random> //`std::mt19937`
#include <iostream> //`std::cout`

static std::mt19937 rng;

static void seed(uint32_t seed) {
    rng.seed(static_cast<std::mt19937::result_type>(seed));
}
static void seed() {
    uint32_t t = static_cast<uint32_t>( time(nullptr) );
    std::hash<uint32_t> hasher; size_t hashed=hasher(t);
    seed( static_cast<uint32_t>(hashed) );
}

int main(int /*argc*/, char* /*argv*/[]) {
    seed();
    std::uniform_int_distribution<> dis(0, 5);
    std::cout << dis(rng);
}

1
मैं आपकी बात से सहमत हूं कि समय के साथ बोना शायद अभ्यास में काफी अच्छा है, अगर आपको इसे सुरक्षित करने की आवश्यकता नहीं है। लेकिन मैं आपके बाकी जवाब से सहमत नहीं हो सकता। समय की हैश के साथ सीडिंग समय के साथ बोने से बेहतर नहीं है।
DW

@DW जाहिर है, यह बहुत बेहतर है। कारण यह है कि हैश असंतत है और फैला मूल्यों की एक अधिक व्यापक रेंज (: के साथ बीज इस खुद की कोशिश है 1और 2और देख सकते हैं कि उनके द्वारा उत्पन्न तैरता के अनुक्रम वास्तव में विचलित कुछ समय लगता है)।
imallett

मुझे नहीं लगता कि यह क्यों मायने रखता है। हम एक समय में केवल एक ही बीज पर चल रहे हैं। बीज (बीज की एन्ट्रॉपी) के लिए संभावित मानों का स्थान एक ही तरीका है - हैशिंग में एन्ट्रापी नहीं बढ़ती है। शायद आप यह सवाल करने के लिए संपादित कर सकते हैं कि हैशिंग बेहतर क्यों है?
DW

0

यहाँ सवाल पर मेरा अपना छुरा है:

#include <random>
#include <chrono>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <iostream>

uint32_t LilEntropy(){
  //Gather many potential forms of entropy and XOR them
  const  uint32_t my_seed = 1273498732; //Change during distribution
  static uint32_t i = 0;        
  static std::random_device rd; 
  const auto hrclock = std::chrono::high_resolution_clock::now().time_since_epoch().count();
  const auto sclock  = std::chrono::system_clock::now().time_since_epoch().count();
  auto *heap         = malloc(1);
  const auto mash = my_seed + rd() + hrclock + sclock + (i++) +
    reinterpret_cast<intptr_t>(heap)    + reinterpret_cast<intptr_t>(&hrclock) +
    reinterpret_cast<intptr_t>(&i)      + reinterpret_cast<intptr_t>(&malloc)  +
    reinterpret_cast<intptr_t>(&LilEntropy);
  free(heap);
  return mash;
}

//Fully seed the mt19937 engine using as much entropy as we can get our
//hands on
void SeedGenerator(std::mt19937 &mt){
  std::uint_least32_t seed_data[std::mt19937::state_size];
  std::generate_n(seed_data, std::mt19937::state_size, std::ref(LilEntropy));
  std::seed_seq q(std::begin(seed_data), std::end(seed_data));
  mt.seed(q);
}

int main(){
  std::mt19937 mt;
  SeedGenerator(mt);

  for(int i=0;i<100;i++)
    std::cout<<mt()<<std::endl;
}

यहाँ विचार XOR का उपयोग करने के लिए एन्ट्रापी के कई संभावित स्रोतों (फास्ट टाइम, स्लो टाइम,, std::random-deviceस्टैटिक वेरिएबल लोकेशन, हीप लोकेशन, फंक्शन लोकेशन, लाइब्रेरी लोकेशन, प्रोग्राम-स्पेसिफिक वैल्यूज़) को मिला कर इनिशियलाइज़ करने के लिए एक बेहतरीन प्रयास है। mt19937। जब तक कम से कम एक बार स्रोत "अच्छा" है, तब तक परिणाम कम से कम "अच्छा" होगा।

यह उत्तर उतना छोटा नहीं है जितना बेहतर होगा और इसमें तर्क की एक या अधिक गलतियाँ हो सकती हैं। इसलिए मैं इस पर काम कर रहा हूं। यदि आपकी प्रतिक्रिया है तो कृपया टिप्पणी करें।


3
पतों में बहुत कम यादृच्छिकता हो सकती है। आपके पास हमेशा समान आवंटन होते हैं, इसलिए छोटे एम्बेडेड सिस्टम पर जहां आप पूरी मेमोरी तक पहुंचते हैं, हर बार एक ही परिणाम प्राप्त करने की संभावना है। मैं कहूंगा कि यह एक बड़ी प्रणाली के लिए काफी अच्छा है, लेकिन एक माइक्रोकंट्रोलर पर गंदगी कर सकता है।
मेन्डेलल

1
मुझे लगता है कि &i ^ &myseedदोनों में से किसी एक की तुलना में काफी कम एन्ट्रापी होनी चाहिए, क्योंकि दोनों एक ही ट्रांसलेशन यूनिट में स्टैटिक स्टोरेज की अवधि वाली वस्तुएं हैं और इसलिए एक साथ पास होने की संभावना है। और आप वास्तव में के आरंभ से विशेष मूल्य का उपयोग नहीं करते हैं myseed?
aschepler

7
टॉट्स में डीलोकेटेड पॉइंटर्स को परिवर्तित करना अपरिभाषित व्यवहार है; जबकि यह अभी भी मौजूद है। ^एक भयानक हैश कॉम्बिनर है; यदि दो मानों में बहुत अधिक एन्ट्रापी है, लेकिन एक दूसरे की तुलना में बहुत कम है, तो यह इसे हटा देता है। +आमतौर पर बेहतर होता है (क्योंकि x + x केवल x में 1 बिट एन्ट्रॉपी को जलाता है, जबकि x ^ x उन सभी को जलाता है)। समारोह सुरक्षित नहीं है मुझे संदेह है ( rd())
यक - एडम नेवरामोंट

2
ओह और +मेरा मतलब है अहस्ताक्षरित ( +हस्ताक्षरित पर यूबी-चारा है)। जबकि ये कुछ हद तक हास्यास्पद यूबी मामले हैं, आपने पोर्टेबल कहा। एक फ़ंक्शन के पते को एक अभिन्न मान के रूप में प्राप्त करने पर विचार करें यदि संभव हो तो (अनिश्चित यदि यह है?)
यक - एडम नेवरामोंट

1
@ मेनडेलल: यहां तक ​​कि एक पूर्ण-शक्ति पीसी पर, हालांकि आवंटन को अलग-अलग भौतिक स्थान मिल सकते हैं (प्रक्रिया के लिए बाहरी मशीन की स्थिति के आधार पर), संकेत प्रक्रिया वर्चुअल एड्रेस स्पेस द्वारा अमूर्त होते हैं, और विशेष रूप से अत्यधिक दोहराए जाने की संभावना होती है। ASLR प्रभाव में नहीं है।
बेन वायगट

0
  • एक छद्म आयामी संख्या जनरेटर (PRNG) बीज के लिए getentropy () का उपयोग करें।
  • यदि आप यादृच्छिक मान (इसके बजाय, कहें, /dev/urandomया /dev/random) चाहते हैं तो getrandom () का उपयोग करें ।

ये लिनक्स, सोलारिस और ओपनबीएसडी जैसे आधुनिक यूनिक्स जैसी प्रणालियों पर उपलब्ध हैं।


-2

किसी दिए गए प्लेटफ़ॉर्म में एंट्रोपी का स्रोत हो सकता है, जैसे कि /dev/random। नैनोकैड के बाद से एपोच std::chrono::high_resolution_clock::now()मानक लाइब्रेरी में सबसे अच्छा बीज है।

मैंने पहले कुछ ऐसे (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )अनुप्रयोगों का उपयोग किया है, जो सुरक्षा-महत्वपूर्ण नहीं हैं।


2
आपको वास्तव /dev/urandomमें इस तरह के एक मामले में उपयोग करना चाहिए । /dev/randomऐसा करने के लिए अच्छे कारणों के बिना ब्लॉक, और अक्सर ([कितने अलग OSes द्वारा अनुमान लगाते हैं कि बाइट्स की यादृच्छिकता का अनुमान / देव / यादृच्छिक]।
अलेक्जेंडर हुज़ैग

2
@AlexanderHuszagh सच है, हालांकि मुझे उन प्रणालियों पर कोड करना पड़ा है जहां /dev/urandomमौजूद नहीं था, और अवरुद्ध करने का विकल्प नियतत्ववाद था। एक बॉक्स हो सकता है /dev/hwrngया /dev/hw_randomसाथ ही, जो और भी बेहतर होना चाहिए।
डेविसलर

ठीक है, मैंने कहा, "जैसे कि /dev/random," और ऐसा लगता है कि लिनक्स पर /dev/randomबनाम के बारे में एक पवित्र युद्ध छिड़ /dev/urandomगया है, जो मैंने उस उदाहरण को देते समय इरादा नहीं किया था ..
डेविसोर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.