इस यादृच्छिक मान का 50/50 के बजाय 25/75 वितरण क्यों है?


139

संपादित करें: तो मूल रूप से जो मैं लिखने की कोशिश कर रहा हूं वह 1 बिट हैश के लिए है double

मैं एक मैप करना चाहते doubleकरने के लिए trueया falseएक 50/50 मौका। उसके लिए मैंने कोड लिखा जो कुछ यादृच्छिक संख्याओं को चुनता है (उदाहरण के रूप में, मैं नियमित रूप से डेटा पर इसका उपयोग करना चाहता हूं और अभी भी 50/50 परिणाम प्राप्त करता हूं) , उनके अंतिम बिट और वेतन वृद्धि की जांच करता है yयदि यह 1 है, या nयदि है 0।

हालाँकि, यह कोड लगातार 25% yऔर 75% परिणाम देता है n। यह 50/50 क्यों नहीं है? और ऐसा अजीब, लेकिन सीधे-आगे (1/3) वितरण क्यों?

public class DoubleToBoolean {
    @Test
    public void test() {

        int y = 0;
        int n = 0;
        Random r = new Random();
        for (int i = 0; i < 1000000; i++) {
            double randomValue = r.nextDouble();
            long lastBit = Double.doubleToLongBits(randomValue) & 1;
            if (lastBit == 1) {
                y++;
            } else {
                n++;
            }
        }
        System.out.println(y + " " + n);
    }
}

उदाहरण आउटपुट:

250167 749833

43
मैं वास्तव में उम्मीद कर रहा हूं कि उत्तर अस्थायी फ्लो-पॉइंट वेरिएंट की यादृच्छिक पीढ़ी के बारे में आकर्षक है, बजाय "LCG कम बिट्स में एन्ट्रापी है"।
स्नेफेल

4
मैं बहुत उत्सुक हूं, "डबल के लिए 1 बिट हैश" का उद्देश्य क्या है? मैं गंभीरता से ऐसी आवश्यकता के किसी भी वैध आवेदन के बारे में नहीं सोच सकता।
corsiKa

3
@corsiKa ज्यामिति अभिकलन में अक्सर दो मामले होते हैं जिन्हें हम दो संभावित उत्तरों (जैसे कि बाईं ओर या रेखा के दाईं ओर?) से चुनने के लिए देख रहे हैं? और कभी-कभी यह तीसरे, पतित मामले (बिंदु) का परिचय देता है। सही लाइन पर), लेकिन आपके पास केवल दो उपलब्ध उत्तर हैं, इसलिए आपको उस मामले में उपलब्ध उत्तरों में से किसी एक को चुनना होगा। सबसे अच्छा तरीका मैं सोच सकता हूं कि दिए गए दोहरे मूल्यों में से एक का 1 बिट हैश लेना (याद रखें, वे ज्यामिति अभिकलन हैं, इसलिए सभी जगह युगल हैं)।
ग्वलासोव

2
@ कोर्सीका (दो में विभाजित टिप्पणी क्योंकि यह बहुत लंबा है) हम कुछ सरल की तरह शुरू कर सकते हैं doubleValue % 1 > 0.5, लेकिन यह बहुत मोटे भी होंगे क्योंकि यह कुछ मामलों में दृश्यमान नियमितता का परिचय दे सकता है (सभी मान लंबाई 1 की सीमा के भीतर हैं)। अगर वह बहुत मोटे हैं, तो क्या हमें शायद छोटी रेंज की कोशिश करनी चाहिए, जैसे doubleValue % 1e-10 > 0.5e-10? सही है। और doubleकम से कम अंतिम मोडुलो के साथ जब तक आप इस दृष्टिकोण का पालन करते हैं, तब तक अंतिम बिट के रूप में होता है।
ग्वलासोव

1
@kmote तब भी आपके पास भारी पक्षपातपूर्ण कम से कम महत्वपूर्ण बिट होगा, और दूसरा बिट इसके लिए क्षतिपूर्ति नहीं करता है - वास्तव में यह शून्य (लेकिन कम तो) के लिए पक्षपाती है, बिल्कुल उसी कारण से। तो वितरण लगभग 50, 12.5, 25, 12.5 होगा। (lastbit & 3) == 0हालांकि, यह काम अजीब होगा।
हेरोल्ड

जवाबों:


165

क्योंकि अगला काम इस तरह से होता है: ( स्रोत )

public double nextDouble()
{
    return (((long) next(26) << 27) + next(27)) / (double) (1L << 53);
}

next(x)xयादृच्छिक बिट्स बनाता है ।

अब यह बात क्यों? क्योंकि पहले भाग (विभाजन से पहले) से उत्पन्न लगभग आधी संख्याएँ इससे कम हैं 1L << 52, और इसलिए उनका महत्व पूरी तरह से 53 बिट्स को नहीं भरता है जो इसे भर सकते हैं, जिसका अर्थ है कि कम से कम महत्वपूर्ण बिट उन लोगों के लिए हमेशा शून्य है।


ध्यान देने की मात्रा के कारण, doubleजावा (और कई अन्य भाषाओं) में वास्तव में कैसा दिखता है और इस प्रश्न में इस पर कोई फर्क क्यों पड़ा, इसकी कुछ अतिरिक्त व्याख्या यहां दी गई है ।

मूल रूप से, doubleइस तरह दिखता है: ( स्रोत )

दोहरा लेआउट

इस चित्र में दिखाई न देने वाला एक बहुत ही महत्वपूर्ण विवरण यह है कि संख्या "सामान्यीकृत" 1 है जैसे कि 53 बिट अंश 1 से शुरू होता है (घातांक का चयन करके ऐसा होता है), कि 1 तब छोड़ा गया है। यही कारण है कि चित्र अंश (महत्व) के लिए 52 बिट्स दिखाता है लेकिन इसमें प्रभावी रूप से 53 बिट्स हैं।

सामान्यीकरण का अर्थ है कि यदि nextDouble53 वें बिट के लिए कोड में सेट किया गया है, तो वह बिट निहित 1 है और यह चला जाता है, और अन्य 52 बिट्स को शाब्दिक रूप से परिणामी के महत्व पर कॉपी किया जाता है double। यदि वह बिट सेट नहीं है, लेकिन शेष बिट्स को तब तक छोड़ दिया जाना चाहिए जब तक कि वह सेट न हो जाए।

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

1: हमेशा नहीं, स्पष्ट रूप से यह शून्य के लिए नहीं किया जा सकता है, जिसकी कोई उच्चतम संख्या नहीं है। इन संख्याओं को असामान्य या उप-असामान्य संख्या कहा जाता है, विकिपीडिया देखें : असामान्य संख्या ।


16
हुर्रे! बस मैं उससे क्या उम्मीद कर रहा था।
स्नेफेल

3
@Matt संभवतः यह एक गति अनुकूलन है। विकल्प एक ज्यामितीय वितरण के साथ प्रतिपादक उत्पन्न करना होगा, और फिर अलग से मंटिसा।
स्नेफेल

7
@ मैट: परिभाषित करें "सर्वश्रेष्ठ।" random.nextDouble()यह आमतौर पर "सबसे अच्छा" तरीका है कि यह किसके लिए है, लेकिन ज्यादातर लोग अपने यादृच्छिक डबल से 1-बिट हैश का उत्पादन करने की कोशिश नहीं कर रहे हैं। क्या आप एक समान वितरण, क्रिप्टोनालिसिस के प्रतिरोध, या क्या देख रहे हैं?
स्ट्रिपिंगवर्यर

1
यह उत्तर बताता है कि यदि ओपी ने यादृच्छिक संख्या को 2 ^ 53 से गुणा किया और जाँच की कि क्या परिणामी पूर्णांक विषम था, तो 50/50 वितरण होगा।
rici

4
@ The111 यह यहाँ कहता है कि nextएक लौटना चाहिए int, इसलिए यह केवल 32 बिट्स तक ही हो सकता है
हेरोल्ड

48

से डॉक्स :

अगली विधि को कक्षा रैंडम द्वारा कार्यान्वित किया जाता है जैसे कि:

public double nextDouble() {
  return (((long)next(26) << 27) + next(27))
      / (double)(1L << 53);
}

लेकिन यह निम्नलिखित पर भी जोर देता है (जोर मेरा):

[जावा के शुरुआती संस्करणों में, परिणाम को गलत तरीके से गणना की गई थी:

 return (((long)next(27) << 27) + next(27))
     / (double)(1L << 54);

यह समतुल्य प्रतीत हो सकता है, यदि बेहतर नहीं है, लेकिन वास्तव में इसने फ्लोटिंग-पॉइंट नंबरों के चक्कर में पूर्वाग्रह के कारण एक बड़ी गैर-बराबरी का परिचय दिया: यह तीन बार की संभावना थी कि महत्व का कम-क्रम बिट 0 होगा उस से यह 1 होगा ! यह गैर-बराबरी शायद व्यवहार में ज्यादा मायने नहीं रखती है, लेकिन हम पूर्णता के लिए प्रयास करते हैं।]

यह नोट जावा 5 के बाद से कम से कम है (जावा के लिए डॉक्स <= 1.4 एक लॉगिनवॉल के पीछे है, जांच के लिए बहुत आलसी)। यह दिलचस्प है, क्योंकि यह समस्या जाहिरा तौर पर जावा 8 में भी मौजूद है। शायद "निश्चित" संस्करण का कभी परीक्षण नहीं किया गया था?


4
अजीब। मैंने अभी इसे जावा 8 पर पुन: पेश किया।
aioobe

1
अब यह दिलचस्प है, क्योंकि मैंने सिर्फ तर्क दिया कि पूर्वाग्रह अभी भी नई पद्धति पर लागू होता है। क्या मै गलत हु?
हेरोल्ड

3
@ हेरोल्ड: नहीं, मुझे लगता है कि आप सही हैं और जिसने भी इस पूर्वाग्रह को ठीक करने की कोशिश की है, वह गलती कर सकता है।
थॉमस

6
@harold समय जावा लोगों को एक ईमेल भेजने के लिए।
डेनियल

8
"शायद निश्चित संस्करण का परीक्षण कभी नहीं किया गया था?" दरअसल, इस पर फिर से विचार करने पर, मुझे लगता है कि डॉक्टर एक अलग समस्या के बारे में थे। ध्यान दें कि इसमें गोलाई का उल्लेख है , जो बताता है कि उन्होंने समस्या के रूप में "तीन बार होने की संभावना" पर विचार नहीं किया था, सीधे, लेकिन इसके बजाय कि यह गैर-समान वितरण की ओर जाता है जब मूल्यों को गोल किया जाता है । ध्यान दें कि मेरे उत्तर में, मेरे द्वारा सूचीबद्ध मूल्य समान रूप से वितरित किए गए हैं, लेकिन IEEE प्रारूप में दर्शाए गए निम्न-क्रम बिट समान नहीं हैं। मुझे लगता है कि उनके द्वारा तय की गई समस्या को समग्र एकरूपता के साथ करना था, न कि कम बिट की एकरूपता से।
अंजब

33

यह परिणाम मुझे आश्चर्यचकित नहीं करता है कि फ्लोटिंग-पॉइंट संख्याओं का प्रतिनिधित्व कैसे किया जाता है। मान लीजिए कि हमारे पास केवल 4 बिट्स के साथ एक बहुत ही कम फ्लोटिंग-पॉइंट प्रकार था। यदि हम 0 और 1 के बीच एक यादृच्छिक संख्या उत्पन्न करते हैं, समान रूप से वितरित किए जाते हैं, तो 16 संभावित मान होंगे:

0.0000
0.0001
0.0010
0.0011
0.0100
...
0.1110
0.1111

अगर ऐसा है कि वे मशीन में कैसे दिखते हैं, तो आप 50/50 वितरण प्राप्त करने के लिए कम-ऑर्डर बिट का परीक्षण कर सकते हैं। हालांकि, IEEE फ़्लोट्स को मंटिसा के 2 गुना की शक्ति के रूप में दर्शाया जाता है; फ्लोट में एक क्षेत्र 2 की शक्ति (प्लस एक निश्चित ऑफसेट) है। 2 की शक्ति को चुना जाता है ताकि "मंटिसा" भाग हमेशा एक नंबर> = 1.0 और <2.0 हो। इसका मतलब यह है कि, वास्तव में, 0.0000इस तरह के अलावा अन्य संख्याओं का प्रतिनिधित्व किया जाएगा:

0.0001 = 2^(-4) x 1.000
0.0010 = 2^(-3) x 1.000
0.0011 = 2^(-3) x 1.100
0.0100 = 2^(-2) x 1.000
... 
0.0111 = 2^(-2) x 1.110
0.1000 = 2^(-1) x 1.000
0.1001 = 2^(-1) x 1.001
...
0.1110 = 2^(-1) x 1.110
0.1111 = 2^(-1) x 1.111

( 1बाइनरी प्वाइंट एक निहित मूल्य है; 32- और 64-बिट फ़्लोट के लिए, वास्तव में इसे धारण करने के लिए कोई बिट आवंटित नहीं किया जाता है1 ।)

लेकिन उपरोक्त को देखते हुए यह प्रदर्शित करना चाहिए कि, यदि आप प्रतिनिधित्व को बिट्स में परिवर्तित करते हैं और कम बिट को देखते हैं, तो आपको शून्य 75% समय मिलेगा। यह 0.5 (बाइनरी 0.1000) से कम सभी मानों के कारण है , जो कि आधे संभावित मान हैं, जिनके मन्तीस शिफ्ट होने के कारण, 0 कम बिट में दिखाई देते हैं। स्थिति अनिवार्य रूप से समान है जब मंटिसा में 52 बिट्स हैं (निहित 1 सहित नहीं) एक के रूप मेंdouble होता है।

(वास्तव में, जैसा कि @sneftel ने एक टिप्पणी में सुझाव दिया था, हम वितरण में 16 से अधिक संभावित मान शामिल कर सकते हैं:

0.0001000 with probability 1/128
0.0001001 with probability 1/128
...
0.0001111 with probability 1/128
0.001000  with probability 1/64
0.001001  with probability 1/64
...
0.01111   with probability 1/32 
0.1000    with probability 1/16
0.1001    with probability 1/16
...
0.1110    with probability 1/16
0.1111    with probability 1/16

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


5
यादृच्छिक बिट्स / बाइट्स / कुछ भी पाने के लिए फ्लोटिंग पॉइंट का उपयोग करना मुझे वैसे भी कंपकंपी देता है। यहां तक ​​कि 0 और n के बीच यादृच्छिक वितरण के लिए, हमारे पास बेहतर विकल्प हैं (arc4random_uniform को देखें) यादृच्छिक * n से ...
mirabilos
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.