यह पहले से पोस्ट किए गए प्रश्न का अनुसरण है:
सी में एक यादृच्छिक संख्या कैसे उत्पन्न करें?
मैं एक विशेष श्रेणी के भीतर से एक यादृच्छिक संख्या उत्पन्न करने में सक्षम होना चाहता हूं, जैसे कि 1 से 6 एक मरने के पक्ष की नकल करना।
मैं ऐसा कैसे कर पाऊंगा?
यह पहले से पोस्ट किए गए प्रश्न का अनुसरण है:
सी में एक यादृच्छिक संख्या कैसे उत्पन्न करें?
मैं एक विशेष श्रेणी के भीतर से एक यादृच्छिक संख्या उत्पन्न करने में सक्षम होना चाहता हूं, जैसे कि 1 से 6 एक मरने के पक्ष की नकल करना।
मैं ऐसा कैसे कर पाऊंगा?
जवाबों:
अब तक के सभी उत्तर गणितीय रूप से गलत हैं। रिटर्निंग rand() % Nएक समान रूप से रेंज में एक नंबर नहीं देता है [0, N)जब तक Nकि अंतराल की लंबाई को विभाजित नहीं करता है जिसमें rand()रिटर्न (यानी 2 की शक्ति) है। इसके अलावा, किसी को भी पता नहीं है कि क्या rand()स्वतंत्र हैं: यह संभव है कि वे जाएं 0, 1, 2, ..., जो एक समान हो लेकिन बहुत यादृच्छिक न हो। एकमात्र धारणा जिसे बनाना उचित लगता है, वह यह है कि rand()एक पॉइसन वितरण होता है: समान आकार के कोई भी दो नॉनओवरलैपिंग सबइंटरवल समान रूप से संभावित और स्वतंत्र होते हैं। मूल्यों के एक निश्चित सेट के लिए, यह एक समान वितरण का तात्पर्य करता है और यह भी सुनिश्चित करता है कि मूल्य rand()अच्छी तरह से बिखरे हुए हैं।
इसका मतलब यह है कि सीमा को बदलने का एकमात्र सही तरीका rand()इसे बक्से में विभाजित करना है; उदाहरण के लिए, यदि RAND_MAX == 11और आप एक सीमा चाहते हैं 1..6, तो आपको {0,1}1, {2,3}2, और इसी तरह से असाइन करना चाहिए । ये असमान हैं, समान रूप से अंतराल हैं और इस प्रकार समान रूप से और स्वतंत्र रूप से वितरित किए जाते हैं।
फ्लोटिंग-पॉइंट डिवीजन का उपयोग करने का सुझाव गणितीय रूप से प्रशंसनीय है, लेकिन सिद्धांत रूप में गोल मुद्दों से ग्रस्त है। शायद doubleयह काम करने के लिए उच्च-पर्याप्त परिशुद्धता है; शायद नहीं। मैं नहीं जानता और मुझे इसका पता नहीं लगाना है; किसी भी मामले में, जवाब प्रणाली पर निर्भर है।
सही तरीका पूर्णांक अंकगणित का उपयोग करना है। यही है, आप निम्नलिखित की तरह कुछ चाहते हैं:
#include <stdlib.h> // For random(), RAND_MAX
// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
unsigned long
// max <= RAND_MAX < ULONG_MAX, so this is okay.
num_bins = (unsigned long) max + 1,
num_rand = (unsigned long) RAND_MAX + 1,
bin_size = num_rand / num_bins,
defect = num_rand % num_bins;
long x;
do {
x = random();
}
// This is carefully written not to overflow
while (num_rand - defect <= (unsigned long)x);
// Truncated division is intentional
return x/bin_size;
}
लूप पूरी तरह से समान वितरण प्राप्त करने के लिए आवश्यक है। उदाहरण के लिए, यदि आपको 0 से 2 तक रैंडम नंबर दिए गए हैं और आप केवल 0 से 1 तक ही चाहते हैं, तो आप बस तब तक खींचते रहेंगे जब तक आपको 2 नहीं मिलते; यह जांचना मुश्किल नहीं है कि यह समान संभावना के साथ 0 या 1 देता है। इस पद्धति का वर्णन उस लिंक में भी किया गया है, जो उनके उत्तर में दिया गया था, हालांकि अलग तरीके से कोडित किया गया था। मैं इसके random()बजाय उपयोग कर रहा हूं rand()क्योंकि इसका बेहतर वितरण है (जैसा कि मैन पेज के लिए नोट किया गया है rand())।
यदि आप डिफ़ॉल्ट रेंज के बाहर यादृच्छिक मान प्राप्त करना चाहते हैं [0, RAND_MAX], तो आपको कुछ मुश्किल करना होगा। शायद सबसे समीचीन एक समारोह को परिभाषित करने के लिए है random_extended()कि खींचती nबिट्स (का उपयोग कर random_at_most()में) और रिटर्न [0, 2**n), और फिर लागू random_at_most()साथ random_extended()के स्थान पर random()(और 2**n - 1के स्थान पर RAND_MAX) की तुलना में कम एक यादृच्छिक मूल्य खींचने के लिए 2**n, यह सोचते हैं आप एक संख्यात्मक प्रकार है कि इस तरह के पकड़ कर सकते हैं एक कीमत। अंत में, निस्संदेह, आप नकारात्मक मूल्यों सहित, [min, max]उपयोग करने में मान प्राप्त कर सकते हैं min + random_at_most(max - min)।
max - min > RAND_MAX, जब मैं ऊपर बताए गए मुद्दे से अधिक गंभीर हो (उदाहरण के लिए वीसी ++ में RAND_MAXकेवल 32767 है)।
do {} while()।
@ रेयान रीच के जवाब के बाद, मैंने सोचा कि मैं अपना क्लीन अप संस्करण पेश करूंगा। पहले सीमा की जाँच के लिए दूसरी सीमा की जाँच की आवश्यकता नहीं है, और मैंने इसे पुनरावृत्ति के बजाय पुनरावृत्त बना दिया है। यह श्रेणी [न्यूनतम, अधिकतम], जहां max >= minऔर में मान लौटाता है 1+max-min < RAND_MAX।
unsigned int rand_interval(unsigned int min, unsigned int max)
{
int r;
const unsigned int range = 1 + max - min;
const unsigned int buckets = RAND_MAX / range;
const unsigned int limit = buckets * range;
/* Create equal size buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
do
{
r = rand();
} while (r >= limit);
return min + (r / buckets);
}
limitएक इंट (और वैकल्पिक रूप से bucketभी) बनाकर समस्या को आसानी से हल किया जाता है । संपादित करें: मैंने प्रस्ताव प्रस्तुत और संपादित किया है। RAND_MAX / rangeINT_MAXbuckets * rangeRAND_MAX
यदि आप किसी सीमा का अधिकतम और न्यूनतम मान जानते हैं, तो यह एक सूत्र है, और आप श्रेणी के बीच में समावेशी संख्याएँ उत्पन्न करना चाहते हैं:
r = (rand() % (max + 1 - min)) + min
intअतिप्रवाह max+1-min।
unsigned int
randr(unsigned int min, unsigned int max)
{
double scaled = (double)rand()/RAND_MAX;
return (max - min +1)*scaled + min;
}
अन्य विकल्पों के लिए यहां देखें ।
(((max-min+1)*rand())/RAND_MAX)+minऔर संभवत: सटीक समान वितरण प्राप्त कर सकते हैं (यह मानते हुए कि RAND_MAX अतिप्रवाहित नहीं होने के सापेक्ष बहुत छोटा है)।
max + 1, यदि या तो rand() == RAND_MAX, या rand()बहुत करीब है RAND_MAXऔर फ्लोटिंग-पॉइंट त्रुटियां अंतिम परिणाम अतीत को धक्का देती हैं max + 1। सुरक्षित होने के लिए, आपको यह देखना चाहिए कि परिणाम इसे वापस करने से पहले सीमा के भीतर है।
RAND_MAX + 1.0। मुझे अभी भी यकीन नहीं है कि max + 1वापसी को रोकने के लिए यह काफी अच्छा है , हालांकि: विशेष रूप से, + minअंत में एक दौर शामिल है जो max + 1रैंड के बड़े मूल्यों के लिए उत्पादन को समाप्त कर सकता है ()। इस दृष्टिकोण को पूरी तरह से छोड़ दें और पूर्णांक अंकगणितीय का उपयोग करें।
RAND_MAXद्वारा RAND_MAX+1.0सुझाए गए अनुसार प्रतिस्थापित किया जाता है, तो मेरा मानना है कि यह सुरक्षित है बशर्ते कि + minपूर्णांक अंकगणितीय का उपयोग करके किया जाए return (unsigned int)((max - min + 1) * scaled) + min:। (गैर स्पष्ट) कारण यह है कि यहां तक कि दौर से डेढ़-टू-, (और यह भी कि यह सोचते हैं आईईईई 754 गणित और है max - min + 1है एक डबल के रूप में वास्तव में प्रदर्शनीय, लेकिन यह एक ठेठ मशीन पर सच हो जाएगा), यह हमेशा सच है कि x * scaled < xके लिए किसी भी सकारात्मक डबल xऔर किसी भी डबल scaledसंतोषजनक 0.0 <= scaled && scaled < 1.0।
randr(0, UINT_MAX): हमेशा 0. उत्पन्न करता है
क्या आप ऐसा नहीं करेंगे:
srand(time(NULL));
int r = ( rand() % 6 ) + 1;
%मापांक ऑपरेटर है। अनिवार्य रूप से यह सिर्फ 6 से विभाजित होगा और शेष ... 0 - 5 से वापस करेगा
rand()जिसमें जनरेटर की स्थिति के कम-क्रम वाले बिट्स शामिल हैं (यदि यह एक एलसीजी का उपयोग करता है)। मैंने अब तक एक भी नहीं देखा है - उनमें से सभी (हाँ, जिसमें RVC_MAX के साथ MSVC सिर्फ 32767 है) कम-ऑर्डर बिट्स को हटा दें । अन्य कारणों से मापांक का उपयोग करने की अनुशंसा नहीं की जाती है, अर्थात् यह वितरण को छोटी संख्या के पक्ष में करता है।
उन लोगों के लिए जो पूर्वाग्रह की समस्या को समझते हैं, लेकिन अस्वीकृति-आधारित विधियों के अप्रत्याशित रन-टाइम को बर्दाश्त नहीं कर सकते, यह श्रृंखला [0, n-1]अंतराल में उत्तरोत्तर कम पक्षपातपूर्ण यादृच्छिक पूर्णांक पैदा करती है :
r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...
यह i * log_2(RAND_MAX + 1)बिट्स (जहां iपुनरावृत्तियों की संख्या है) के एक उच्च-सटीक निश्चित-बिंदु यादृच्छिक संख्या का संश्लेषण करके और एक लंबे गुणा का प्रदर्शन करके ऐसा करता है n।
जब बिट्स की संख्या पर्याप्त रूप से बड़ी होती है n, तो पूर्वाग्रह बहुत छोटा हो जाता है।
इससे कोई फर्क नहीं पड़ता कि RAND_MAX + 1क्या n( इस प्रश्न के अनुसार ) से कम है , या यदि यह दो की शक्ति नहीं है, लेकिन RAND_MAX * nबड़े होने पर पूर्णांक अतिप्रवाह से बचने के लिए देखभाल की जानी चाहिए ।
RAND_MAXअक्सर होता है INT_MAX, इसलिए RAND_MAX + 1-> UB (जैसे INT_MIN)
RAND_MAX * nबड़े होने पर पूर्णांक अतिप्रवाह से बचने के लिए देखभाल की जानी चाहिए "। आपको अपनी आवश्यकताओं के लिए उपयुक्त प्रकारों का उपयोग करने की व्यवस्था करने की आवश्यकता है।
RAND_MAXअक्सर INT_MAX" हाँ, लेकिन केवल 16 बिट सिस्टम पर! किसी भी कारण से आधुनिक आर्किटेकचर INT_MAX2 ^ 32/2 और 2 ^ 16/2 पर रखा जाएगा RAND_MAX। क्या यह गलत धारणा है?
intसंकलक का परीक्षण किया , मुझे RAND_MAX == 32767एक RAND_MAX == 2147483647पर और दूसरे पर मिला । मेरा समग्र अनुभव (दशकों) वह RAND_MAX == INT_MAXअधिक बार है। तो इससे सहमत नहीं है कि एक यथोचित आधुनिक 32-बिट संरचना निश्चित रूप से एक होगा RAND_MAXपर 2^16 / 2। चूंकि सी कल्पना की अनुमति देता है 32767 <= RAND_MAX <= INT_MAX, मैं प्रवृत्ति के बजाय उस रास्ते को कोड करता हूं।
मोडुलो पूर्वाग्रह (अन्य उत्तरों में सुझाए गए) से बचने के लिए आप हमेशा उपयोग कर सकते हैं:
arc4random_uniform(MAX-MIN)+MIN
जहाँ "MAX" ऊपरी बाउंड है और "MIN" कम बाउंड है। उदाहरण के लिए, 10 और 20 के बीच की संख्याओं के लिए:
arc4random_uniform(20-10)+10
arc4random_uniform(10)+10
सरल उपाय और "रैंड ()% एन" का उपयोग करने से बेहतर है।
#include <bsd/stdlib.h>सबसे पहले आपको ध्यान देने योग्य है । इसके अलावा, किसी भी विचार कैसे बिना MinGW या CygWin के साथ विंडोज पर प्राप्त करने के लिए?
यहाँ रेयान रीच के समाधान की तुलना में थोड़ा सरल एल्गोरिथ्म है:
/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
uint32_t range = (end - begin) + 1;
uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);
/* Imagine range-sized buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
uint32_t randVal = rand();
while (randVal >= limit) randVal = rand();
/// Return the position you hit in the bucket + begin as random number
return (randVal % range) + begin;
}
Example (RAND_MAX := 16, begin := 2, end := 7)
=> range := 6 (1 + end - begin)
=> limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)
The limit is always a multiple of the range,
so we can split it into range-sized buckets:
Possible-rand-output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Buckets: [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
Buckets + begin: [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]
1st call to rand() => 13
→ 13 is not in the bucket-range anymore (>= limit), while-condition is true
→ retry...
2nd call to rand() => 7
→ 7 is in the bucket-range (< limit), while-condition is false
→ Get the corresponding bucket-value 1 (randVal % range) and add begin
=> 3
RAND_MAX + 1आसानी से अतिप्रवाह intजोड़ सकते हैं । उस स्थिति में, (RAND_MAX + 1) % rangeसंदिग्ध परिणाम उत्पन्न करेगा। विचार करें(RAND_MAX + (uint32_t)1)
जबकि रयान सही है, यादृच्छिकता के स्रोत के बारे में जो ज्ञात है, उसके आधार पर समाधान बहुत सरल हो सकता है। समस्या को फिर से बताने के लिए:
[0, MAX)समान वितरण के साथ पूर्णांक संख्याओं को आउटपुट करना ।[rmin, rmax], जहां 0 <= rmin < rmax < MAX।मेरे अनुभव में, यदि डिब्बे की संख्या (या "बक्से") मूल संख्याओं की सीमा से काफी कम है, और मूल स्रोत क्रिप्टोग्राफिक रूप से मजबूत है - तो उस सभी कठोरता, और सरल मोड्यूलर डिवीजन से गुजरने की कोई आवश्यकता नहीं है प्रत्यय (जैसे output = rnd.next() % (rmax+1), यदि rmin == 0), और यादृच्छिक संख्याएं उत्पन्न होती हैं जो समान रूप से "पर्याप्त" वितरित की जाती हैं, और गति के किसी भी नुकसान के बिना। मुख्य कारक यादृच्छिकता स्रोत है (यानी, बच्चों, घर पर यह कोशिश मत करो rand())।
यहाँ एक उदाहरण / प्रमाण है कि यह व्यवहार में कैसे काम करता है। मैं 1 से 22 तक यादृच्छिक संख्या उत्पन्न करना चाहता था, जिसमें क्रिप्टोग्राफिक रूप से मजबूत स्रोत था जो यादृच्छिक बाइट्स (इंटेल RDRAND के आधार पर) का उत्पादन करता था। परिणाम हैं:
Rnd distribution test (22 boxes, numbers of entries in each box): 1: 409443 4.55% 2: 408736 4.54% 3: 408557 4.54% 4: 409125 4.55% 5: 408812 4.54% 6: 409418 4.55% 7: 408365 4.54% 8: 407992 4.53% 9: 409262 4.55% 10: 408112 4.53% 11: 409995 4.56% 12: 409810 4.55% 13: 409638 4.55% 14: 408905 4.54% 15: 408484 4.54% 16: 408211 4.54% 17: 409773 4.55% 18: 409597 4.55% 19: 409727 4.55% 20: 409062 4.55% 21: 409634 4.55% 22: 409342 4.55% total: 100.00%
यह मेरे उद्देश्य के लिए समान रूप से समान है, जैसे कि मुझे अपने उद्देश्य (उचित पासा फेंक, WWII सिफर मशीनों जैसे http://users.telenet.be/d.rijmenants/en/kl-7sim.htm , आदि के लिए क्रिप्टोग्राफिक रूप से मजबूत कोडबुक बनाने की आवश्यकता है) )। आउटपुट कोई प्रशंसनीय पूर्वाग्रह नहीं दिखाता है।
यहाँ क्रिप्टोग्राफिकली स्ट्रॉन्ग (ट्रू) रैंडम नंबर जेनरेटर का स्रोत है: इंटेल डिजिटल रैंडम नंबर जेनरेटर और एक सैंपल कोड जो 64-बिट (अहस्ताक्षरित) रैंडम नंबर का उत्पादन करता है।
int rdrand64_step(unsigned long long int *therand)
{
unsigned long long int foo;
int cf_error_status;
asm("rdrand %%rax; \
mov $1,%%edx; \
cmovae %%rax,%%rdx; \
mov %%edx,%1; \
mov %%rax, %0;":"=r"(foo),"=r"(cf_error_status)::"%rax","%rdx");
*therand = foo;
return cf_error_status;
}
मैंने इसे clang-6.0.1 (स्ट्रेट), और gcc-4.8.3 के साथ "-Wa, q" फ्लैग (क्योंकि GAS इन नए निर्देशों का समर्थन नहीं करता है) के साथ मैक OS X पर संकलित किया।
gcc randu.c -o randu -Wa,q(GCC 5.3.1 उबंटू 16 पर) के साथ संकलित या clang randu.c -o randu(क्लैंग 3.8.0) काम करता है, लेकिन रनटाइम पर कोर को डंप करता है Illegal instruction (core dumped)। कोई विचार?
rand()। मैंने कुछ परीक्षणों की कोशिश की और इस प्रश्न को पोस्ट किया लेकिन मुझे अभी तक कोई निश्चित उत्तर नहीं मिला है।
जैसा कि मॉडुलो के पहले कहा गया था, क्योंकि यह वितरण को सीमित नहीं करता है। मेरे कोड का उपयोग करता है जो बिट्स को मास्क करता है और यह सुनिश्चित करने के लिए उनका उपयोग करता है कि वितरण तिरछा नहीं है।
static uint32_t randomInRange(uint32_t a,uint32_t b) {
uint32_t v;
uint32_t range;
uint32_t upper;
uint32_t lower;
uint32_t mask;
if(a == b) {
return a;
}
if(a > b) {
upper = a;
lower = b;
} else {
upper = b;
lower = a;
}
range = upper - lower;
mask = 0;
//XXX calculate range with log and mask? nah, too lazy :).
while(1) {
if(mask >= range) {
break;
}
mask = (mask << 1) | 1;
}
while(1) {
v = rand() & mask;
if(v <= range) {
return lower + v;
}
}
}
निम्नलिखित सरल कोड आपको वितरण को देखने देता है:
int main() {
unsigned long long int i;
unsigned int n = 10;
unsigned int numbers[n];
for (i = 0; i < n; i++) {
numbers[i] = 0;
}
for (i = 0 ; i < 10000000 ; i++){
uint32_t rand = random_in_range(0,n - 1);
if(rand >= n){
printf("bug: rand out of range %u\n",(unsigned int)rand);
return 1;
}
numbers[rand] += 1;
}
for(i = 0; i < n; i++) {
printf("%u: %u\n",i,numbers[i]);
}
}
v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;मैं समझता हूं कि मॉडुलो मास्किंग की तुलना में बहुत धीमा ऑपरेशन है, लेकिन मुझे अभी भी लगता है ..... इसका परीक्षण किया जाना चाहिए।
rand()intसीमा में रिटर्न देता है [0..RAND_MAX]। यह श्रेणी आसानी से एक सबरेंज हो सकती है uint32_tऔर फिर randomInRange(0, ,b)रेंज में कभी भी मान उत्पन्न नहीं करती है (INT_MAX...b]।