रैंड () + रैंड () नकारात्मक संख्या क्यों उत्पन्न करता है?


304

मैंने उस rand()लाइब्रेरी फंक्शन का अवलोकन किया जब इसे केवल एक बार लूप के भीतर बुलाया जाता है, यह लगभग हमेशा सकारात्मक संख्या पैदा करता है।

for (i = 0; i < 100; i++) {
    printf("%d\n", rand());
}

लेकिन जब मैं दो rand()कॉल जोड़ता हूं , तो उत्पन्न संख्याओं में नकारात्मक संख्याएं अधिक होती हैं।

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", rand(), (rand() + rand()));
}

क्या कोई समझा सकता है कि मैं दूसरे मामले में नकारात्मक संख्या क्यों देख रहा हूं?

पुनश्च: मैं लूप से पहले बीज को इनिशियलाइज़ करता हूं srand(time(NULL))


11
rand()नकारात्मक नहीं हो सकता ...
ट्वेंटीलेमोन

293
रैंड () + रैंड () owerflow कर सकते हैं
मस्कैकोवनिक

13
RAND_MAXआपके कंपाइलर के लिए क्या है ? आप आमतौर पर इसे पा सकते हैं stdlib.h। (मजेदार: जाँच man 3 rand, यह एक लाइन विवरण "खराब यादृच्छिक संख्या जनरेटर" को
सहन करता है

6
हर समझदार प्रोग्रामर क्या करेगा abs(rand()+rand())। मैं बल्कि एक नकारात्मक एक से एक सकारात्मक यूबी होगा! ;)
विनीसियस कामाकुरा

11
@ हेक्सा: जो कि यूबी के लिए कोई नपुंसकता नहीं है, जैसा कि पहले से ही जोड़ के लिए होता है। आप UB को परिभाषित व्यवहार नहीं बना सकते । एक समझदार progrtammer नरक की तरह यूबी से बच जाएंगे।
इस साइट के लिए बहुत ही ईमानदार

जवाबों:


542

rand()बीच का एक पूर्णांक वापस जाने के लिए परिभाषित किया गया है 0और RAND_MAX

rand() + rand()

ओवरफ्लो हो सकता है। आप जो देखते हैं, वह पूर्णतः ओवरफ़्लो के कारण अपरिभाषित व्यवहार का परिणाम है ।


4
@JakubArnold: कैसे अतिप्रवाह व्यवहार प्रत्येक भाषा द्वारा अलग-अलग तरीके से निर्दिष्ट किया जाता है? उदाहरण के लिए पायथन में कोई नहीं है (ठीक है, उपलब्ध मेमोरी तक), जैसे कि इंट अभी बढ़ता है।
इस साइट

2
@Olaf यह निर्भर करता है कि हस्ताक्षरित पूर्णांक का प्रतिनिधित्व करने के लिए कोई भाषा कैसे निर्णय लेती है। जावा में पूर्णांक अतिप्रवाह (जावा 8 तक) का पता लगाने के लिए कोई तंत्र नहीं था और इसे चारों ओर लपेटने के लिए परिभाषित किया गया था और गो केवल 2 के पूरक प्रतिनिधित्व का उपयोग करता है और इसे हस्ताक्षरित पूर्णांक ओवरफ्लो के लिए कानूनी रूप से परिभाषित करता है। सी स्पष्ट रूप से 2 के पूरक से अधिक का समर्थन करता है।
पीपी

2
@ EvanCarslake नहीं, यह एक सार्वभौमिक व्यवहार नहीं है। आप जो कहते हैं वह 2 के पूरक प्रतिनिधित्व के बारे में है। लेकिन सी भाषा अन्य अभ्यावेदन के लिए भी अनुमति देती है। C भाषा विनिर्देश कहता है कि पूर्णांक ओवरफ़्लो हस्ताक्षरित अपरिभाषित है । तो सामान्य तौर पर, किसी भी कार्यक्रम को इस तरह के व्यवहार पर भरोसा नहीं करना चाहिए और हस्ताक्षरित पूर्णांक अतिप्रवाह का कारण न बनने के लिए सावधानीपूर्वक कोड करने की आवश्यकता है। लेकिन यह अहस्ताक्षरित पूर्णांकों के लिए लागू नहीं है क्योंकि वे एक अच्छी तरह से परिभाषित (कमी मॉडुलो 2) तरीके से "रैप-अराउंड" करेंगे। [जारी] ...
पीपी

12
यह हस्ताक्षरित पूर्णांक अतिप्रवाह से संबंधित सी मानक का उद्धरण है: यदि अभिव्यक्ति के मूल्यांकन के दौरान एक असाधारण स्थिति होती है (अर्थात, यदि परिणाम गणितीय रूप से परिभाषित नहीं है या अपने प्रकार के लिए प्रतिनिधित्व योग्य मूल्यों की सीमा में नहीं है), तो व्यवहार अपरिभाषित है।
पीपी

3
@EvanCarslake सी कंपाइलर मानक का उपयोग करते हैं और हस्ताक्षर किए पूर्णांक के लिए सवाल से थोड़ा हटकर वे मान सकते हैं कि a + b > aअगर उन्हें पता है कि b > 0। वे यह भी मान सकते हैं कि यदि बाद में निष्पादित बयान है a + 5तो वर्तमान मूल्य कम है INT_MAX - 5। ट्रैप्स प्रोग्राम के बिना 2 के पूरक प्रोसेसर / दुभाषिया पर भी ऐसा व्यवहार नहीं किया जा सकता है जैसे कि intट्रैप के बिना 2 के पूरक थे।
मैकीज पाइचोटका

90

समस्या इसके अतिरिक्त है। rand()का intमान लौटाता है 0...RAND_MAX। इसलिए, यदि आप उनमें से दो जोड़ते हैं, तो आप उठेंगे RAND_MAX * 2। यदि यह अधिक हो जाता है INT_MAX, तो अतिरिक्त सीमा का परिणाम एक सीमा से अधिक intहो सकता है। हस्ताक्षरित मूल्यों का अतिप्रवाह अपरिभाषित व्यवहार है और इससे आपके कीबोर्ड को विदेशी भाषा में बात करने में परेशानी हो सकती है।

जैसा कि दो यादृच्छिक परिणामों को जोड़ने में यहाँ कोई लाभ नहीं है, सरल विचार सिर्फ यह नहीं करना है। वैकल्पिक रूप से आप प्रत्येक परिणाम को unsigned intजोड़ सकते हैं इससे पहले कि अगर वह योग पकड़ सके। या एक बड़े प्रकार का उपयोग करें। ध्यान दें कि longहै जरूरी नहीं कि की तुलना में व्यापक intहै, वही पर लागू होता है long long, तो intकम से कम 64 बिट्स है!

निष्कर्ष: बस जोड़ से बचें। यह अधिक "यादृच्छिकता" प्रदान नहीं करता है। यदि आपको अधिक बिट्स की आवश्यकता है, तो आप मानों को संक्षिप्त कर सकते हैं sum = a + b * (RAND_MAX + 1), लेकिन यह भी संभावना है कि डेटा प्रकार की तुलना में अधिक की आवश्यकता हो int

जैसा कि आपका बताया गया कारण शून्य-परिणाम से बचना है: दो rand()कॉल के परिणामों को जोड़कर टाला नहीं जा सकता , क्योंकि दोनों शून्य हो सकते हैं। इसके बजाय, आप सिर्फ वेतन वृद्धि कर सकते हैं। यदि RAND_MAX == INT_MAX, ऐसा नहीं किया जा सकता है int। हालांकि, (unsigned int)rand() + 1बहुत, बहुत संभावना है। समान रूप से (निश्चित रूप से नहीं), क्योंकि इसकी आवश्यकता होती है UINT_MAX > INT_MAX, जो उन सभी कार्यान्वयनों पर सच है जिनके बारे में मुझे पता है (जो कि पिछले कुछ वर्षों में काफी कुछ एम्बेडेड आर्किटेक्चर, डीएसपी और सभी डेस्कटॉप, मोबाइल और सर्वर प्लेटफॉर्म को कवर करता है)।

चेतावनी:

यद्यपि पहले से ही यहां टिप्पणियों में छिड़का हुआ है, कृपया ध्यान दें कि दो यादृच्छिक मूल्यों को जोड़ने से एक समान वितरण नहीं मिलता है, लेकिन एक त्रिकोणीय वितरण जैसे दो पासा रोलिंग: 12(दो पासा) पाने के लिए दोनों पासा दिखाना होगा 6। के लिए 11पहले से ही दो संभावित रूप हैं: 6 + 5या 5 + 6, आदि।

तो, इस पहलू से भी जोड़ खराब है।

यह भी ध्यान दें कि परिणाम rand()उत्पन्न एक दूसरे से स्वतंत्र नहीं हैं, क्योंकि वे एक छद्म आयामी संख्या जनरेटर द्वारा उत्पन्न होते हैं । यह भी ध्यान दें कि मानक गणना मूल्यों की गुणवत्ता या एक समान वितरण को निर्दिष्ट नहीं करता है।


14
@ सबमद: तो क्या होगा अगर दोनों कॉल 0 पर लौटें?
इस साइट के लिए बहुत ही ईमानदार

3
@ वद्मद: मुझे आश्चर्य है कि अगर UINT_MAX > INT_MAX != falseमानक द्वारा गारंटी है। (लगता है की संभावना है, लेकिन यदि आवश्यक हो तो यकीन नहीं है)। यदि हां, तो आप केवल एक परिणाम और वेतन वृद्धि (उस क्रम में!) कर सकते हैं।
इस साइट के लिए बहुत ही ईमानदार

3
जब आप एक गैर-समान वितरण चाहते हैं तो कई यादृच्छिक संख्याओं को जोड़ने में लाभ होता है: stackoverflow.com/questions/30492259/…
C

6
0 से बचने के लिए, एक सरल "जबकि परिणाम 0 है, फिर से रोल करें"?
ओलिवियर दुलक

2
न केवल उन्हें 0 से बचने के लिए एक बुरा तरीका जोड़ रहा है, बल्कि यह एक गैर-समान वितरण में भी परिणत होता है। आपको पासा पलटने के परिणाम जैसा एक वितरण मिलता है: 7 6 बार 2 या 12. के रूप में संभव है
बमर

36

यह इस उत्तर के लिए टिप्पणी में किए गए प्रश्न के स्पष्टीकरण का उत्तर है ,

कारण मैं जोड़ रहा था कि मेरे कोड में यादृच्छिक संख्या के रूप में '0' से बचें। रैंड () + रैंड () त्वरित गंदा समाधान था जो आसानी से मेरे दिमाग में आया था।

समस्या से बचने के लिए 0. प्रस्तावित समाधान के साथ (कम से कम) दो समस्याएं हैं। एक है, जैसा कि अन्य उत्तर इंगित करते हैं, कि rand()+rand()अपरिभाषित व्यवहार को लागू कर सकते हैं। सबसे अच्छी सलाह है कि अपरिभाषित व्यवहार कभी न करें। एक और मुद्दा यह है कि कोई गारंटी नहीं है कि rand()एक पंक्ति में दो बार 0 का उत्पादन नहीं होगा।

निम्नलिखित शून्य को अस्वीकार करता है, अपरिभाषित व्यवहार से बचा जाता है, और अधिकांश मामलों में दो कॉल की तुलना में अधिक तेज़ होगा rand():

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);

9
किस बारे में rand() + 1?
प्रश्नकर्ता

3
@askvictor अतिप्रवाह कर सकता है (हालांकि यह संभावना नहीं है)।
गेरिट

3
@gerrit - MAX_INT और RAND_MAX पर निर्भर करता है
पूछनेवाला

3
@gerrit, मुझे आश्चर्य होगा अगर वे समान नहीं हैं , लेकिन मुझे लगता है कि यह बच्चों के लिए एक जगह है :)
Askvictor

10
यदि RAND_MAX == MAX_INT, रैंड () + 1 रैंड के मान के समान ही संभाव्यता के साथ ओवरफ्लो हो जाएगा (0), जो इस समाधान को पूरी तरह से व्यर्थ बनाता है। यदि आप इसे जोखिम में
डालने

3

मूल रूप rand()से और आपके मामले में , के बीच 0और संख्या का उत्पादन करते हैं।RAND_MAX2 RAND_MAX > INT_MAX

आप अतिप्रवाह को रोकने के लिए अपने डेटा-प्रकार के अधिकतम मूल्य के साथ मापांक कर सकते हैं। यह संभोग यादृच्छिक संख्याओं के वितरण को बाधित करेगा, लेकिन randत्वरित यादृच्छिक संख्या प्राप्त करने का एक तरीका है।

#include <stdio.h>
#include <limits.h>

int main(void)
{
    int i=0;

    for (i=0; i<100; i++)
        printf(" %d : %d \n", rand(), ((rand() % (INT_MAX/2))+(rand() % (INT_MAX/2))));

    for (i=0; i<100; i++)
        printf(" %d : %ld \n", rand(), ((rand() % (LONG_MAX/2))+(rand() % (LONG_MAX/2))));

    return 0;
}

2

हो सकता है कि आप यह सुनिश्चित करने की कोशिश कर सकते हैं कि 2 रैंड () के योग से लौटाया गया मान कभी भी RAND_MAX के मान से अधिक न हो। एक संभावित दृष्टिकोण योग हो सकता है = रैंड () / 2 + रैंड () / 2; यह सुनिश्चित करेगा कि 32767 के RAND_MAX मान के साथ 16 बिट संकलक के लिए, यदि दोनों रैंड 32767 वापस करने के लिए होता है, तो भी (32767/2 = 16383) 16383 + 16383 = 32766, इस प्रकार नकारात्मक योग नहीं होगा।


1
ओपी परिणामों से 0 को बाहर करना चाहता था। इसके अलावा यादृच्छिक मूल्यों का एक समान वितरण प्रदान नहीं करता है।
इस साइट

@ ओलाफ: इस बात की कोई गारंटी नहीं है कि दो लगातार कॉल करने rand()से दोनों की उपज शून्य नहीं होगी, इसलिए शून्य से बचने की इच्छा दो मूल्यों को जोड़ने का एक अच्छा कारण नहीं है। दूसरी ओर, एक गैर-समान वितरण की इच्छा दो यादृच्छिक मूल्यों को जोड़ने का एक अच्छा कारण होगा यदि कोई सुनिश्चित करता है कि अतिप्रवाह नहीं होता है।
सुपरकाट

1

कारण मैं जोड़ रहा था कि मेरे कोड में यादृच्छिक संख्या के रूप में '0' से बचें। रैंड () + रैंड () त्वरित गंदा समाधान था जो आसानी से मेरे दिमाग में आया था।

एक सरल समाधान (ठीक है, इसे "हैक" कहें) जो कभी भी शून्य परिणाम नहीं देता है और कभी भी अतिप्रवाह नहीं होता है:

x=(rand()/2)+1    // using divide  -or-
x=(rand()>>1)+1   // using shift which may be faster
                  // compiler optimization may use shift in both cases

यह आपके अधिकतम मूल्य को सीमित कर देगा, लेकिन अगर आप उस बारे में परवाह नहीं करते हैं, तो यह आपके लिए ठीक काम करना चाहिए।


1
सिडेनोट: हस्ताक्षरित चर के सही बदलाव के साथ सावधान। यह केवल गैर-संवैधानिक मूल्यों के लिए अच्छी तरह से परिभाषित है, नकारात्मक के लिए, इसे लागू किया गया है। (सौभाग्य से, rand()हमेशा एक नॉनवेजेटिव वैल्यू लौटाता है)। हालाँकि, मैं कम्पाइलर के यहाँ अनुकूलन छोड़ दूँगा।
इस साइट के लिए बहुत ही ईमानदार

@ ओलाफ: सामान्य रूप से, दो द्वारा हस्ताक्षरित विभाजन एक पारी से कम कुशल होगा। जब तक एक संकलक लेखक संकलक कि बताने में प्रयास का निवेश किया है randगैर नकारात्मक हो जाएगा, पाली एक हस्ताक्षरित पूर्णांक 2. प्रभाग द्वारा द्वारा विभाजन से अधिक कुशल हो जाएगा 2uकाम कर सकता है, लेकिन अगर xएक है intअहस्ताक्षरित से अंतर्निहित रूपांतरण के बारे में चेतावनी में हो सकता है दस्तखत करना।
सुपरकैट

@ सुपरकैट: कृपया मेरी टिप्पणी को फिर से पढ़ें। आपको बहुत अच्छी तरह से पता होना चाहिए कि कोई भी उचित संकलक किसी भी तरह के लिए एक बदलाव का उपयोग करेगा / 2(मैंने इसे कुछ के लिए भी देखा है -O0, जैसे कि अनुकूलन के बिना अनुरोध किए गए)। यह संभवतः सी कोड का सबसे तुच्छ और सबसे अधिक स्थापित अनुकूलन है। बिंदु पूरी तरह से पूर्णांक रेंज के लिए मानक द्वारा परिभाषित किया गया है, न केवल नकारात्मक मान। फिर से: कंपाइलर को ऑप्टिमाइज़ेशन छोड़ें, पहली जगह में सही और स्पष्ट कोड लिखें । शुरुआती लोगों के लिए यह और भी महत्वपूर्ण है।
इस साइट के लिए बहुत ईमानदार

@ ओलाफ: मैंने जो भी कंपाइलर टेस्ट किया है वह एक से ज्यादा शिफ्ट करने पर rand()या 2uइस्तेमाल करने पर भी 2 से डिवाइड करने पर राइट शिफ्टिंग से ज्यादा कुशल कोड जेनरेट करता है -O3। एक व्यक्ति कह सकता है कि इस तरह के अनुकूलन से कोई फर्क नहीं पड़ता है, लेकिन यह कहना कि "कंपाइलर के लिए इस तरह के अनुकूलन छोड़ दें" का अर्थ यह होगा कि कंपाइलर उनके प्रदर्शन की संभावना रखते हैं। क्या आप किसी भी संकलक के बारे में जानते हैं जो वास्तव में होगा?
सुपरकैट

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

1

0 से बचने के लिए, यह प्रयास करें:

int rnumb = rand()%(INT_MAX-1)+1;

आपको शामिल करने की आवश्यकता है limits.h


4
यह प्राप्त करने की संभावना को दोगुना कर देगा। यह मूल रूप से एक ही (लेकिन कब्जे वाले धीमी) सशर्त रूप से 1 जोड़ रहा है यदि rand()पैदावार 0.
इस साइट के लिए बहुत ईमानदार है

हां, आप सही हैं ओलाफ। अगर रैंड () = 0 या INT_MAX -1 द रंबल 1. होगा
डोनी

इससे भी बदतर, जैसा कि मैं इसके बारे में सोचने के लिए आता हूं। यह वास्तव में 1और 2(सभी मान लिया गया RAND_MAX == INT_MAX) के लिए प्रवृत्ति को दोगुना कर देगा । मैं के बारे में भूल गया था - 1
इस साइट के लिए बहुत ईमानदार

1
-1यहाँ कोई मूल्य कार्य करता है। rand()%INT_MAX+1; अभी भी केवल [1 ... INT_MAX] श्रेणी में मान उत्पन्न करेगा।
chux -

-2

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

जिन लोगों ने रैंड () + 1 को पोस्ट किया है, वे समाधान का उपयोग कर रहे हैं जो गारंटी देने के लिए सबसे अधिक उपयोग करते हैं कि उन्हें एक नकारात्मक संख्या नहीं मिलती है। लेकिन, यह दृष्टिकोण वास्तव में सबसे अच्छा तरीका भी नहीं है।

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

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


2
rand()क्या बीज का उपयोग करने के लिए निर्दिष्ट नहीं करता है। मानक इसे किसी भी समय का संबंध नहीं, एक छद्म आयामी जनरेटर का उपयोग करने के लिए निर्दिष्ट करता है । यह जनरेटर की योग्यता के बारे में भी नहीं बताता है। वास्तविक समस्या स्पष्ट रूप से अतिप्रवाह है। ध्यान दें कि rand()+1बचने के लिए उपयोग किया जाता है 0; rand()एक नकारात्मक मूल्य वापस नहीं करता है। क्षमा करें, लेकिन आप यहाँ बिंदु से चूक गए। यह PRNG की गुणवत्ता के बारे में नहीं है। ...
इस साइट

... GNU / Linux के तहत इसके बीज से /dev/randomअच्छे PRNG का उपयोग करें और ( rand()glibc से गुणवत्ता के बारे में निश्चित नहीं ) या डिवाइस का उपयोग जारी रखें - यदि आपके पास पर्याप्त एन्ट्रापी उपलब्ध नहीं है तो ब्लॉक करने के लिए अपने एप्लिकेशन को जोखिम में डालकर इसका उपयोग जारी रखें। आवेदन में अपना एन्ट्रापी प्राप्त करने की कोशिश करना बहुत अच्छी तरह से भेद्यता हो सकती है क्योंकि संभवतः हमला करना आसान है। और अब यह सख्त हो गया है - यहाँ नहीं
इस साइट के लिए बहुत ही ईमानदार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.