मैक की तुलना में लिनक्स पर रैंड () कितनी बार दोहराता है?


86

जब मैं काम कर रहा था और जब मैंने देखा कि rand()लिनक्स पर मैक की तुलना में कहीं अधिक बार संख्याओं को दोहराता प्रतीत होता है, तो मैं इसे परीक्षण करने के लिए यादृच्छिक आवेषण का उपयोग कर रहा हूं और सी में एक हैशमैप को सी में लागू कर रहा था । RAND_MAXदोनों प्लेटफार्मों पर 2147483647 / 0x7FFFFFFF है। मैंने इसे इस परीक्षण कार्यक्रम में कम कर दिया है, जो एक बाइट सरणी- RAND_MAX+1लंबा बनाता है , RAND_MAXयादृच्छिक संख्याओं को उत्पन्न करता है , यदि प्रत्येक एक डुप्लिकेट है, तो नोट करता है और सूची के अनुसार इसे जांचता है।

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main() {
    size_t size = ((size_t)RAND_MAX) + 1;
    char *randoms = calloc(size, sizeof(char));
    int dups = 0;
    srand(time(0));
    for (int i = 0; i < RAND_MAX; i++) {
        int r = rand();
        if (randoms[r]) {
            // printf("duplicate at %d\n", r);
            dups++;
        }
        randoms[r] = 1;
    }
    printf("duplicates: %d\n", dups);
}

लिनक्स लगातार 790 मिलियन डुप्लिकेट बनाता है। मैक लगातार केवल एक उत्पन्न करता है, इसलिए यह प्रत्येक यादृच्छिक संख्या के माध्यम से लूप करता है कि यह लगभग दोहराए बिना उत्पन्न कर सकता है । क्या कोई मुझे समझा सकता है कि यह कैसे काम करता है? मैं मैन पेजों से अलग कुछ भी नहीं बता सकता, यह नहीं बता सकता कि कौन सा आरएनजी प्रत्येक का उपयोग कर रहा है, और ऑनलाइन कुछ भी नहीं मिल सकता है। धन्यवाद!


4
चूंकि रैंड () 0.RAND_MAX समावेशी मानों को लौटाता है, आपके सरणी को RAND_MAX + 1 का आकार देने की आवश्यकता है
ब्लास्टफ़र्नेस

21
आपने देखा होगा कि RAND_MAX / e ~ = 790 मिलियन। इसके अलावा की सीमा (1-1 / n) ^ n के रूप में n दृष्टिकोण अनंत है 1 / e।
डेविड श्वार्ट्ज

3
@DavidSchwartz यदि मैं आपको सही ढंग से समझता हूं, तो यह बता सकता है कि लिनक्स पर संख्या लगभग 790 मिलियन है। मुझे लगता है कि सवाल यह है: क्यों / कैसे मैक कई बार दोहराता नहीं है?
थेरॉन एस

26
रनटाइम लाइब्रेरी में PRNG के लिए कोई गुणवत्ता की आवश्यकता नहीं है। केवल वास्तविक आवश्यकता उसी बीज के साथ पुनरावृत्ति है। जाहिर है, आपके लिनक्स में PRNG की गुणवत्ता आपके मैक से बेहतर है।
शाम

4
@chux हाँ, लेकिन चूंकि यह गुणा पर आधारित है, राज्य कभी शून्य नहीं हो सकता है और परिणाम (अगला राज्य) भी शून्य होगा। स्रोत कोड के आधार पर यह शून्य के लिए एक विशेष मामले के रूप में जांच करता है यदि शून्य के साथ वरीयता प्राप्त है, लेकिन यह कभी भी अनुक्रम के भाग के रूप में शून्य का उत्पादन नहीं करता है।
अर्कुको

जवाबों:


118

पहली बार में यह MacOS तरह लग सकता है rand()किसी भी तरह बेहतर किसी भी संख्या को दोहरा नहीं करने के लिए है, एक नोट करना चाहिए कि संख्या की इस राशि उत्पन्न के साथ किया जाता है की उम्मीद है, चारों ओर (डुप्लिकेट के बहुत सारे वास्तव में देखने के लिए 790 मिलियन, या (2 31 -1 ) / )। इसी तरह क्रम से संख्याओं के माध्यम से पुनरावृत्ति भी कोई डुप्लिकेट नहीं होगा, लेकिन बहुत यादृच्छिक नहीं माना जाएगा। तो लिनक्स rand()कार्यान्वयन इस परीक्षण में एक सच्चे यादृच्छिक स्रोत से अप्रभेद्य है, जबकि macOS rand()नहीं है।

एक और बात जो पहली नज़र में आश्चर्यचकित करती है वह यह है कि मैकओएस rand()इतनी अच्छी तरह से डुप्लिकेट से बचने का प्रबंधन कैसे कर सकता है। इसके स्रोत कोड को देखते हुए , हम कार्यान्वयन को निम्नानुसार पाते हैं:

/*
 * Compute x = (7^5 * x) mod (2^31 - 1)
 * without overflowing 31 bits:
 *      (2^31 - 1) = 127773 * (7^5) + 2836
 * From "Random number generators: good ones are hard to find",
 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
 * October 1988, p. 1195.
 */
    long hi, lo, x;

    /* Can't be initialized with 0, so use another value. */
    if (*ctx == 0)
        *ctx = 123459876;
    hi = *ctx / 127773;
    lo = *ctx % 127773;
    x = 16807 * lo - 2836 * hi;
    if (x < 0)
        x += 0x7fffffff;
    return ((*ctx = x) % ((unsigned long) RAND_MAX + 1));

यह वास्तव में 1 और RAND_MAX, समावेशी के बीच की सभी संख्याओं में परिणत होता है , अनुक्रम से पहले एक बार फिर से दोहराता है। चूंकि अगला राज्य गुणा पर आधारित है, राज्य कभी भी शून्य नहीं हो सकता है (या सभी भविष्य के राज्य भी शून्य होंगे)। इस प्रकार आपके द्वारा देखी गई दोहराई गई संख्या पहले वाली है, और शून्य वह है जो कभी वापस नहीं आता है।

Apple अपने दस्तावेज़ीकरण और उदाहरणों में कम से कम तब तक के लिए बेहतर यादृच्छिक संख्या जनरेटर के उपयोग को बढ़ावा दे रहा है जब तक macOS (या OS X) मौजूद है, इसलिए गुणवत्ता rand()को संभवतः महत्वपूर्ण नहीं माना जाता है, और वे सिर्फ एक विस्तार के साथ फंस गए हैं सबसे सरल छद्म आयामी जनरेटर उपलब्ध हैं। (जैसा कि आपने उल्लेख किया, उनके बजाय rand()उपयोग arc4random()करने की सिफारिश के साथ भी टिप्पणी की गई है ।)

संबंधित नोट पर, सबसे सरल छद्म आयामी संख्या जनरेटर मैं पा सकता हूं कि यादृच्छिकता के लिए इस (और कई अन्य) परीक्षणों में अच्छे परिणाम उत्पन्न होते हैं * : xorshift *

uint64_t x = *ctx;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
*ctx = x;
return (x * 0x2545F4914F6CDD1DUL) >> 33;

यह कार्यान्वयन आपके परीक्षण में लगभग 790 मिलियन डुप्लिकेट का परिणाम देता है।


5
1980 में प्रकाशित एक पत्रिका का लेख "जन्मदिन की समस्या" के आधार पर PRNGs के लिए एक सांख्यिकीय परीक्षण प्रस्तावित है।
पीजे

14
"Apple अपने प्रलेखन में बेहतर यादृच्छिक संख्या जनरेटर के उपयोग को बढ़ावा दे रहा है" -> बेशक Apple arc4random()पीछे कोड की तरह काम कर सकता है rand()और एक अच्छा rand()परिणाम प्राप्त कर सकता है । प्रोग्रामर को अलग-अलग कोड करने की कोशिश करने के बजाय, बस बेहतर लाइब्रेरी फ़ंक्शन बनाएं। "वे बस अटक गए हैं" उनकी पसंद है।
chux -

22
मैक में एक निरंतर ऑफसेट की कमी rand()इसे इतना बुरा बना देती है कि यह व्यावहारिक उपयोग के लिए उपयोगी नहीं है: रैंड ()% 7 हमेशा 0 वापस क्यों करता है? , रैंड ()% 14 केवल मानों को 6 या 13
फुलव्यू

4
@PeterCordes: इस तरह की आवश्यकता है rand, कि इसे उसी बीज के साथ फिर से चलाने से समान अनुक्रम उत्पन्न होता है। ओपनबीएसडी randटूट गया है और इस अनुबंध का पालन नहीं करता है।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

8
@ R..GitHubSTOPHELPINGICE क्या आपको सी की आवश्यकता दिखाई देती है कि rand()एक ही बीज से लाइब्रेरी के विभिन्न संस्करणों के बीच एक ही क्रम उत्पन्न होता है? लाइब्रेरी संस्करणों के बीच प्रतिगमन परीक्षण के लिए ऐसी गारंटी उपयोगी हो सकती है, फिर भी मुझे इसके लिए कोई सी आवश्यकता नहीं है।
chux - मोनिका

33

MacOS stdlib में एक अनिर्दिष्ट रैंड () फ़ंक्शन प्रदान करता है। यदि आप इसे अनदेखा छोड़ देते हैं, तो यह पहला मान जो आउटपुट करता है वह 16807, 282475249, 1622650073, 984943658 और 1144108930 हैं। एक त्वरित खोज से पता चलेगा कि यह क्रम बहुत ही मूल LCG यादृच्छिक संख्या जनरेटर से मेल खाती है जो निम्न सूत्र की पुनरावृति करता है:

x n +1 = 7 5 · x n (mod 2 31 - 1)

चूंकि इस आरएनजी की स्थिति पूरी तरह से एकल 32-बिट पूर्णांक के मान से वर्णित है, इसलिए इसकी अवधि बहुत लंबी नहीं है। सटीक होने के लिए, यह हर 2 31 - 2 पुनरावृत्तियों को दोहराता है , 1 से 2 31 - 2 तक हर मूल्य को आउटपुट करता है ।

मुझे नहीं लगता कि लिनक्स के सभी संस्करणों के लिए रैंड () का एक मानक कार्यान्वयन है, लेकिन एक शानदार रैंड () फ़ंक्शन है जो अक्सर उपयोग किया जाता है। एक एकल 32-बिट राज्य चर के बजाय, यह 1000 से अधिक बिट्स के एक पूल का उपयोग करता है, जो सभी इरादों और उद्देश्यों के लिए पूरी तरह से दोहराए जाने वाले अनुक्रम का उत्पादन नहीं करेगा। फिर, आप शायद यह पता लगा सकते हैं कि इस आरएनजी से पहले कुछ आउटपुट प्रिंट करके आपके पास कौन सा संस्करण है, इसे पहले बिना बोए। (Glibc रैंड () फ़ंक्शन 1804289383, 846930886, 1681692777, 1714636915 और 1957747793 नंबर का उत्पादन करता है।)

तो आप लिनक्स में (और शायद ही कोई मैकओएस में) अधिक टकराव का कारण यह है कि रैंड का लिनक्स संस्करण () मूल रूप से अधिक यादृच्छिक है।


5
एक अदक्ष के rand()साथ एक व्यवहार करना चाहिएsrand(1);
pmg

5
rand()MacOS का स्रोत कोड उपलब्ध है: filesource.apple.com/source/Libc/Libc-1353.11.2/stdlib/FreeBSD/… FWIW, मैंने स्रोत से इस संकलन के खिलाफ एक ही परीक्षण चलाया और वास्तव में इसका परिणाम है केवल एक ही नकल। Apple arc4random()अपने उदाहरणों और दस्तावेज़ीकरण में अन्य यादृच्छिक संख्या जनरेटर (जैसे कि स्विफ्ट को संभालने से पहले) के उपयोग को बढ़ावा दे रहा है , इसलिए rand()उनके प्लेटफार्मों पर देशी ऐप्स में इसका उपयोग संभवतः बहुत आम नहीं है, जो समझा सकता है कि यह बेहतर क्यों नहीं है।
अर्कुओ

उत्तर के लिए धन्यवाद, जो मेरे प्रश्न का उत्तर देता है। और (2 ^ 31) -2 की अवधि बताती है कि यह आखिर में क्यों दोहराएगा जैसे मैंने देखा था। आप (@ r3mainer) ने कहा rand()था कि यह अनिर्दिष्ट है, लेकिन @ अर्कु ने स्पष्ट स्रोत का लिंक प्रदान किया है। क्या आप में से किसी को पता है कि मुझे अपने सिस्टम पर वह फाइल क्यों नहीं मिल रही है, और मैं int rand(void) __swift_unavailable("Use arc4random instead.");मैक में ही क्यों देख रहा हूं stdlib.h? मुझे लगता है कि कोड @Arkku से जुड़ा है बस में संकलित है ... क्या पुस्तकालय?
थेरॉन एस

1
@TheronS इसे C लाइब्रेरी, libc, में संकलित किया गया है /usr/lib/libc.dylib। =)
अर्कुको

5
जिनमें से संस्करण rand()दिए गए सी कार्यक्रम का उपयोग करता है "संकलक" या "ऑपरेटिंग सिस्टम", बल्कि सी मानक पुस्तकालय के कार्यान्वयन के द्वारा निर्धारित नहीं है (जैसे, glibc, libc.dylib, msvcrt*.dll)।
पीटर ओ।

10

rand()सी मानक द्वारा परिभाषित किया गया है, और सी मानक निर्दिष्ट नहीं करता है कि किस एल्गोरिदम का उपयोग करना है। जाहिर है, Apple आपके GNU / Linux कार्यान्वयन के लिए एक अवर एल्गोरिथ्म का उपयोग कर रहा है: लिनक्स एक आपके परीक्षण में एक सच्चे यादृच्छिक स्रोत से अप्रभेद्य है, जबकि Apple कार्यान्वयन बस संख्याओं को फेरबदल करता है।

यदि आप किसी भी गुणवत्ता के यादृच्छिक संख्याएँ चाहते हैं, तो या तो एक बेहतर PRNG का उपयोग करें, जो उस संख्या की गुणवत्ता पर कम से कम कुछ गारंटियाँ देता है, जो वापस आती है, या केवल /dev/urandomया इसी तरह से पढ़ी जाती है । बाद में आपको क्रिप्टोग्राफिक गुणवत्ता वाले नंबर मिलते हैं, लेकिन यह धीमा है। यहां तक ​​कि अगर यह अपने आप से बहुत धीमा है, तो /dev/urandomकुछ अन्य, तेजी से PRNG को कुछ उत्कृष्ट बीज प्रदान कर सकते हैं।


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

: btw यहाँ एक क्रिप्टोग्राफी द्वारा यादृच्छिक संख्या जनरेटर सुरक्षित का एक उदाहरण है github.com/divinity76/phpcpp/commit/... - लेकिन यह की सी ++ के बजाय सी और मैं दे रहा हूँ एसटीएल कार्यान्वयन सब करने के बड़े कार्य करने ..
hanshenrik

3
@hanshenrik एक क्रिप्टो RNG आमतौर पर एक साधारण हैश तालिका के लिए ओवरकिल और बहुत धीमी है।
PM 2Ring

1
@ PM2Ring बिल्कुल। एक हैश तालिका हैश मुख्य रूप से तेज होना चाहिए, अच्छा नहीं। हालाँकि, यदि आप एक हैश टेबल एल्गोरिथ्म विकसित करना चाहते हैं जो न केवल तेज़ हो, बल्कि सभ्य भी हो, तो मेरा मानना ​​है कि क्रिप्टोग्राफ़िक हैश एल्गोरिदम के कुछ ट्रिक्स जानना फायदेमंद है। यह आपको सबसे अधिक चमकने वाली गलतियों से बचने में मदद करेगा जो कि सबसे तेज हैश एल्गोरिदम को पहेली बनाते हैं। फिर भी, मैंने यहां एक विशिष्ट कार्यान्वयन के लिए विज्ञापन नहीं दिया होगा।
विस्फ़ोटक - मोनिका

@ cmaster काफी सच है। यह निश्चित रूप से मिश्रण कार्यों और हिमस्खलन प्रभाव जैसी चीजों के बारे में थोड़ा जानने के लिए एक अच्छा विचार है । सौभाग्य से गैर-क्रिप्टो हैश फ़ंक्शन अच्छे गुणों के साथ हैं जो बहुत अधिक गति (जब सही तरीके से लागू होते हैं) का त्याग नहीं करते हैं, जैसे xxhash, murmur3, या siphash।
PM 2Ring

5

सामान्य तौर पर, रैंड / सरैंड की जोड़ी को लंबे समय के लिए कम-क्रम बिट्स के कारण पदावनत किया जाता है, क्योंकि परिणामों में उच्च-ऑर्डर बिट्स की तुलना में कम यादृच्छिकता प्रदर्शित होती है। यह आपके परिणामों के साथ कुछ भी कर सकता है या नहीं हो सकता है, लेकिन मुझे लगता है कि यह याद रखने के लिए अभी भी एक अच्छा अवसर है कि भले ही कुछ रैंड / सरैंड कार्यान्वयन अब तक हो गए हैं, पुराने कार्यान्वयन जारी हैं और यादृच्छिक का उपयोग करना बेहतर है (3) )। मेरे आर्क लिनक्स बॉक्स पर, रैंड (3) के लिए निम्नलिखित नोट अभी भी मैन पेज में है:

  The versions of rand() and srand() in the Linux C Library use the  same
   random number generator as random(3) and srandom(3), so the lower-order
   bits should be as random as the higher-order bits.  However,  on  older
   rand()  implementations,  and  on  current implementations on different
   systems, the lower-order bits are much less random than the  higher-or-
   der bits.  Do not use this function in applications intended to be por-
   table when good randomness is needed.  (Use random(3) instead.)

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

सामान्य तौर पर, यदि आप मानक पुस्तकालय से कुछ का उपयोग करने जा रहे हैं, तो यादृच्छिक का उपयोग करें यदि आप कर सकते हैं (मैन पेज इसे POSIX के रूप में POSIX.1-2001 पर मानक के रूप में सूचीबद्ध करता है, लेकिन रैंड मानक तरीका है जो C से पहले भी मानकीकृत था) । या बेहतर अभी तक, खुली न्यूमेरिकल रेसिपी को क्रैक करें (या ऑनलाइन देखें) या नथ और एक को लागू करें। वे वास्तव में आसान हैं और आपको केवल उन विशेषताओं के साथ एक सामान्य उद्देश्य RNG के लिए एक बार ऐसा करने की आवश्यकता है जो आपको सबसे अधिक बार चाहिए और जो कि ज्ञात गुणवत्ता का है।


संदर्भ के लिए धन्यवाद। मुझे वास्तव में उच्च-गुणवत्ता वाले यादृच्छिकता की आवश्यकता नहीं है, और Rust में MT19937 को लागू किया है। ज्यादातर यह जानने के लिए उत्सुक थे कि दोनों प्लेटफॉर्म अलग-अलग व्यवहार क्यों करते हैं।
थेरॉन एस

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

यह मजेदार है कि सलाह रैंड () को बेहतर बनाने के बजाय "रैंड () का उपयोग करने से रोकने" की थी। मानक में कुछ भी कभी नहीं कहता है कि इसे एक विशिष्ट जनरेटर होना चाहिए।
पाइप

2
@ पिप यदि rand()'बेहतर' बनाने का मतलब होगा कि यह धीमा हो जाएगा (जो कि यह संभवतः - क्रिप्टोग्राफिक रूप से सुरक्षित यादृच्छिक संख्याओं में बहुत प्रयास करता है), तो संभवतः इसे तेजी से रखना बेहतर है, भले ही थोड़ा अधिक पूर्वानुमान हो। बिंदु में मामला: हमारे पास एक उत्पादन एप्लिकेशन था जिसे शुरू करने में उम्र लग गई थी, जिसे हमने एक आरएनजी से पता लगाया था, जिसके आरंभ में पर्याप्त एन्ट्रापी के लिए प्रतीक्षा करने की आवश्यकता थी ... यह पता चला कि इसे इतना सुरक्षित होने की आवश्यकता नहीं थी, इसलिए इसे बदलकर एक 'बदतर' आरएनजी एक बड़ा सुधार था।
गीदड़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.