कैसे कुशलतापूर्वक बड़े, समान रूप से वितरित, बेतरतीब पूर्णांकों को बैश में उत्पन्न करें?


30

मैं सोच कर दिया है क्या प्राप्त करने के लिए सबसे अच्छा तरीका है अच्छा बैश, यानी में अनियमितता, एक प्रक्रिया के बीच एक यादृच्छिक सकारात्मक पूर्णांक प्राप्त करने के लिए किया जाएगा क्या MINऔर MAXऐसा है कि

  1. सीमा मनमाने ढंग से बड़ी हो सकती है (या कम से कम, कहते हैं, 2 32 -1 तक);
  2. मान समान रूप से वितरित किए जाते हैं (अर्थात, कोई पूर्वाग्रह नहीं);
  3. यह कुशल है।

बैश में यादृच्छिकता प्राप्त करने का एक प्रभावी तरीका $RANDOMचर का उपयोग करना है। हालांकि, यह केवल 0 और 2 15 -1 के बीच एक मूल्य का नमूना देता है , जो सभी उद्देश्यों के लिए पर्याप्त बड़ा नहीं हो सकता है। लोग आमतौर पर एक modulo का उपयोग उस सीमा में प्राप्त करने के लिए करते हैं जिसे वे चाहते हैं, जैसे,

MIN=0
MAX=12345
rnd=$(( $RANDOM % ($MAX + 1 - $MIN) + $MIN ))

यह, इसके अलावा, एक पूर्वाग्रह बनाता है जब तक कि $MAX2 15 -1 = 32767 को विभाजित करने के लिए नहीं होता है । जैसे, यदि $MIN0 है और $MAX9 है, तो 7 से मान 0 के मान 8 और 9 की तुलना में थोड़ा अधिक संभावित है, जैसा $RANDOMकि 32768 या 32769 कभी नहीं होगा। यह पूर्वाग्रह सीमा बढ़ने के साथ खराब हो जाता है, उदाहरण के लिए, यदि $MIN0 और $MAXहै। 9999 है, तो संख्या 0 2767 के माध्यम से की सम्भावना है 4 / 32767 , जबकि संख्या 2768 9999 के माध्यम से ही की सम्भावना है 3 / 32767

इसलिए जब उपरोक्त विधि शर्त 3 ​​को पूरा करती है, तो यह शर्तों 1 और 2 को पूरा नहीं करती है।

1 और 2 की शर्तों को पूरा करने की कोशिश में अब तक की सबसे अच्छी विधि, जो /dev/urandomइस प्रकार है:

MIN=0
MAX=1234567890
while
  rnd=$(cat /dev/urandom | tr -dc 0-9 | fold -w${#MAX} | head -1 | sed 's/^0*//;')
  [ -z $rnd ] && rnd=0
  (( $rnd < $MIN || $rnd > $MAX ))
do :
done

मूल रूप से, बस यादृच्छिकता इकट्ठा करें /dev/urandom( /dev/randomइसके बजाय उपयोग करने के लिए विचार कर सकते हैं यदि क्रिप्टोग्राफिक रूप से मजबूत छद्म आयामी संख्या जनरेटर वांछित है, और यदि आपके पास बहुत समय है, या शायद एक हार्डवेयर यादृच्छिक संख्या जनरेटर), तो हर वर्ण को हटाएं जो एक दशमलव अंक नहीं है, तह। की लंबाई के लिए उत्पादन $MAXऔर 0 की अग्रणी कटौती। अगर हम केवल 0 प्राप्त करने के लिए हुआ है तो $rndखाली है, इसलिए इस मामले में सेट rndकरें 0। जांचें कि क्या परिणाम हमारी सीमा के बाहर है और यदि ऐसा है, तो दोहराएं। मैंने पहरे में "लूप" के शरीर को मजबूर कर दिया, ताकि do ... whileलूप का अनुकरण करने की भावना में कम से कम एक बार शरीर के निष्पादन को मजबूर कर दिया जाए , क्योंकि rndशुरुआत करने के लिए अपरिभाषित है।

मुझे लगता है कि मैंने यहां 1 और 2 की शर्तों को पूरा किया है, लेकिन अब मैंने स्थिति खराब कर दी है। यह थोड़े धीमी है। एक सेकंड या तो (एक सेकंड का दसवां जब मैं भाग्यशाली हूं) तक ले जाता हूं। दरअसल, लूप को समाप्त करने की गारंटी भी नहीं दी जाती है (हालांकि समय बढ़ने के साथ समाप्ति की संभावना 1 में परिवर्तित हो जाती है)।

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

जवाब की तालिका

  1. सबसे बुनियादी (और इसलिए पोर्टेबल) विचार केवल लंबे समय तक एक यादृच्छिक बिटस्ट्रिंग उत्पन्न करना है। वहाँ एक यादृच्छिक bitstring पैदा करने के विभिन्न तरीके हैं, या तो का उपयोग कर रहे हैं बैश के अंतर्निहित $RANDOMचर या का उपयोग करते हुए odऔर /dev/urandom(या /dev/random)। यदि यादृच्छिक संख्या से अधिक है $MAX, तो शुरू करें।

  2. वैकल्पिक रूप से, बाहरी उपकरणों का उपयोग करना संभव है।

    • पर्ल समाधान
      • प्रो: काफी पोर्टेबल, सरल, लचीला
      • कॉन्ट्रा: 2 32 -1 से ऊपर की बड़ी संख्या के लिए नहीं
    • अजगर का हल
      • प्रो: सरल, लचीला, बड़ी संख्या के लिए भी काम करता है
      • कॉन्ट्रा: कम पोर्टेबल
    • Zsh समाधान
      • प्रो: वैसे भी जो लोग zsh का उपयोग करते हैं उनके लिए अच्छा है
      • कॉन्ट्रा: शायद कम पोर्टेबल भी

Base64 को रैंडम बिट्स के बजाय केवल पूर्णांक क्यों निकालते हैं, फिर बेस 64 से एन्कोडेड फॉर्म से बेस 10 तक वर्णों की एक निश्चित संख्या (आवश्यक सीमा के आधार पर) परिवर्तित करना?
मूरू

क्या इसे कोसने की जरूरत है? क्या ऐसा कुछ rand=$(command)करना चाहिए यदि commandआपकी आवश्यकताओं को पूरा करने वाला एक iteger लौटाता है?
terdon

@ मुरु यह वास्तव में एक अच्छा विचार है। मैंने एक समान विचार पर कुछ सोचा था, dd if=/dev/urandom 2>/dev/nullजिसका उपयोग करके और पाइपिंग के माध्यम से od -t d(बेस 64 के माध्यम से चक्कर से बचा जाता है), लेकिन यह मेरे लिए स्पष्ट नहीं है कि रूपांतरण कैसे होता है और क्या यह वास्तव में निष्पक्ष है। यदि आप अपने विचार को एक कुशल, कामकाजी स्क्रिप्ट में विस्तारित कर सकते हैं और समझा सकते हैं कि कोई पूर्वाग्रह क्यों नहीं है, तो यह एक महान जवाब के लिए बना देगा। :)
माल्ट स्कॉरप्पा

@terdon मैं बैश पसंद करूंगा। मेरा मतलब है, निश्चित रूप से आप अपनी पसंदीदा भाषा को लागू कर सकते हैं pythonया कर सकते perlहैं, लेकिन यह हर जगह उपलब्ध नहीं है। मैं कुछ अधिक पोर्टेबल पसंद करेंगे। ठीक है, awkयादृच्छिक समारोह ठीक होगा, मुझे लगता है। लेकिन अधिक पोर्टेबल, बेहतर :)
Malte Skoruppa

2
हाँ, मैं की तर्ज पर सोच रहा था perl -e 'print int(rand(2**32-1))');। यह बहुत सुंदर पोर्टेबल है और बहुत तेज़ होगा। अधिकांश बीजगणित एक ही बीज से शुरू होने के बाद अक्क इसे नहीं काटेगा। तो आपको बाद के रनों पर समान यादृच्छिक संख्या मिलती है। यह केवल एक ही रन के भीतर बदलता है।
terdon

जवाबों:


17

मुझे यहाँ से एक और दिलचस्प विधि दिखाई देती है

rand=$(openssl rand 4 | od -DAn)

यह एक अच्छा विकल्प भी लगता है। यह यादृच्छिक डिवाइस से 4 बाइट पढ़ता है और उन दोनों के बीच अहस्ताक्षरित पूर्णांक के रूप में प्रारूप 0और 2^32-1

rand=$(od -N 4 -t uL -An /dev/urandom | tr -d " ")


odअलग-अलग आदेश क्यों हैं । दोनों सिर्फ 4-बाइट्स अहस्ताक्षरित पूर्णांक प्रिंट करते हैं: 1 - से ओपनसेल, 2 - से /dev/random
jfs

1
@ रमेश मैंने /dev/urandomइसके बजाय उपयोग करने के लिए संपादित किया /dev/random- मुझे उपयोग करने का कोई कारण नहीं दिखता है /dev/random, और यह वास्तव में महंगा / धीमा हो सकता है, या सिस्टम के अन्य भागों को धीमा कर सकता है। (बेझिझक वापस संपादित करें और समझाएं कि क्या वास्तव में इसकी आवश्यकता है।)
वोल्कर सीगल

1
कोई चिंता नहीं, यह वास्तव में आश्चर्यजनक है कि इस साधारण अंतर में इतने जटिल प्रभाव हैं। इसलिए मैंने उदाहरण को सही में बदलने के लिए जोर दिया - लोग उदाहरणों से सीखते हैं।
वोल्कर सीगल

1
@MalteSkoruppa: इसका Iमतलब है sizeof(int)कि 4सिद्धांत से कम हो सकता है । btw, के लिए od -DAnविफल रहता है, (2**32-1)लेकिन od -N4 -tu4 -Anकाम करना जारी रखता है।
jfs

8

आपके सभी शानदार जवाब के लिए आप सभी का धन्यवाद। मैं निम्नलिखित समाधान के साथ समाप्त हुआ, जिसे मैं साझा करना चाहूंगा।

इससे पहले कि मैं whys और hows के बारे में किसी भी अधिक विस्तार में जाऊं, यहां tl; dr : मेरी चमकदार नई स्क्रिप्ट :-)

#!/usr/bin/env bash
#
# Generates a random integer in a given range

# computes the ceiling of log2
# i.e., for parameter x returns the lowest integer l such that 2**l >= x
log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

# uses $RANDOM to generate an n-bit random bitstring uniformly at random
#  (if we assume $RANDOM is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 60 bits
get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

# alternative implementation of get_n_rand_bits:
# uses /dev/urandom to generate an n-bit random bitstring uniformly at random
#  (if we assume /dev/urandom is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 56 bits
get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

# for parameter max, generates an integer in the range {0..max} uniformly at random
# max can be an arbitrary integer, needs not be a power of 2
rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

# MAIN SCRIPT

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

# need absolute value of diff since min (and also max) may be negative
diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

उस पर सहेजें ~/bin/randऔर आपके पास अपनी उपलब्धता है जो किसी अनियंत्रित श्रेणी में पूर्णांक का नमूना कर सकती है। सीमा में नकारात्मक और सकारात्मक पूर्णांक हो सकते हैं और लंबाई में 2 60 -1 तक हो सकते हैं:

$ rand 
Usage: rand [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
$ rand 1 10
9
$ rand -43543 -124
-15757
$ rand -3 3
1
$ for i in {0..9}; do rand $((2**60-1)); done
777148045699177620
456074454250332606
95080022501817128
993412753202315192
527158971491831964
336543936737015986
1034537273675883580
127413814010621078
758532158881427336
924637728863691573

अन्य उत्तरदाताओं द्वारा सभी विचार महान थे। द्वारा जवाब terdon , जेएफ सेबस्टियन , और jimmij इस्तेमाल किया बाहरी उपकरण के लिए एक सरल और कुशल तरीके से काम करने के लिए। हालांकि, मैंने अधिकतम पोर्टेबिलिटी के लिए एक सच्चे बैश समाधान को प्राथमिकता दी, और शायद थोड़ा सा, बस बैश के लिए प्यार से बाहर;)

रमेश के और l0b0 के उत्तर का उपयोग किया है /dev/urandomया के /dev/randomसाथ संयोजन में od। हालांकि, यह अच्छा है, उनके दृष्टिकोणों को केवल 0 से 2 8n की सीमा में यादृच्छिक पूर्णांकों को नमूना करने में सक्षम होने का नुकसान था -1 के लिए कुछ एन के लिए , क्योंकि इस विधि के नमूने बाइट्स, अर्थात, लंबाई के बिटस्ट्रिंग 8. ये काफी बड़े कूदते हैं बढ़ती हुई n।

अंत में, फाल्को का जवाब सामान्य विचार का वर्णन करता है कि यह कैसे मनमाने ढंग से पर्वतमाला के लिए किया जा सकता है (न केवल दो की शक्तियां)। मूल रूप से, एक दी गई सीमा के लिए {0..max}, हम यह निर्धारित कर सकते हैं कि दो की अगली शक्ति क्या है, यानी, बिटस्ट्रिंग के रूप में प्रतिनिधित्व करने के लिए कितने बिट्स की आवश्यकता होती है max। तब हम बस कई बिट्स का नमूना ले सकते हैं और देख सकते हैं कि यह बिस्टरिंग, एक पूर्णांक के रूप में, से अधिक है max। यदि हां, तो दोहराएं। चूंकि हम प्रतिनिधित्व करने के लिए आवश्यक जितने भी बिट्स का नमूना लेते हैं max, प्रत्येक पुनरावृत्ति में 50% सफल होने की संभावना या 50% (सबसे खराब स्थिति में 50%, सर्वश्रेष्ठ मामले में 100%) के बराबर संभावना होती है। तो यह बहुत कुशल है।

मेरी स्क्रिप्ट मूल रूप से फाल्को के उत्तर का एक ठोस कार्यान्वयन है, जो शुद्ध बैश में लिखा गया है और अत्यधिक कुशल है क्योंकि यह वांछित लंबाई के बिटस्ट्रिंग्स का नमूना करने के लिए बैश के अंतर्निहित बिटवाइज़ संचालन का उपयोग करता है। इसके अलावा, यह एलिया कागन द्वारा एक विचार का सम्मान करता है जो $RANDOMबार-बार होने वाले चालान से उत्पन्न होने वाले बिटस्ट्रिंग को अंतर्निहित चर का उपयोग करने का सुझाव देता है $RANDOM। मैंने वास्तव में उपयोग करने की संभावनाओं को लागू किया /dev/urandomऔर $RANDOM। डिफ़ॉल्ट रूप से उपरोक्त स्क्रिप्ट का उपयोग करता है $RANDOM। (और ठीक है, अगर /dev/urandomहम का उपयोग करने की जरूरत है od और tr , लेकिन ये POSIX द्वारा समर्थित हैं।)

तो यह कैसे काम करता है?

इससे पहले कि मैं इसमें शामिल होऊं, दो अवलोकन:

  1. यह बताता है कि बैश 2 63 -1 से बड़े पूर्णांकों को नहीं संभाल सकता । अपने आप को देखो:

    $ echo $((2**63-1))
    9223372036854775807
    $ echo $((2**63))
    -9223372036854775808

    ऐसा प्रतीत होता है कि बेज़ आंतरिक रूप से पूर्णांक को संग्रहीत करने के लिए हस्ताक्षरित 64-बिट पूर्णांक का उपयोग करता है। तो, 2 63 पर यह "चारों ओर घूमता है" और हमें एक नकारात्मक पूर्णांक मिलता है। इसलिए हम जो भी रैंडम फ़ंक्शन का उपयोग करते हैं उसके साथ किसी भी रेंज को 2 63 -1 से बड़ा पाने की उम्मीद नहीं कर सकते हैं। बैश बस इसे संभाल नहीं सकते।

  2. जब भी हम एक मनमाने ढंग से सीमा के बीच minऔर maxसंभवतः के साथ एक मूल्य का नमूना करना चाहते हैं min != 0, तो हम बस के बीच 0और max-minइसके बजाय एक मूल्य का नमूना कर सकते हैं और फिर minअंतिम परिणाम में जोड़ सकते हैं । यह काम करता है भले ही minऔर संभवतः नकारात्मक भी maxहो , लेकिन हमें इसके और पूर्ण मूल्य के बीच एक मूल्य के नमूने के लिए सावधान रहना होगा । तो, हम इस पर ध्यान केंद्रित कर सकते हैं कि कैसे और एक मनमाने ढंग से धनात्मक पूर्णांक के बीच यादृच्छिक मूल्य का नमूना लिया जाए । बाकी काम आसान है।0 max-min0max

चरण 1: एक पूर्णांक (लघुगणक) का प्रतिनिधित्व करने के लिए कितने बिट्स की आवश्यकता है यह निर्धारित करें

इसलिए किसी दिए गए मूल्य के लिए max, हम यह जानना चाहते हैं कि बिटस्ट्रिंग के रूप में इसका प्रतिनिधित्व करने के लिए कितने बिट्स की आवश्यकता है। ऐसा इसलिए है कि बाद में हम बेतरतीब ढंग से केवल नमूने के रूप में कई बिट्स की आवश्यकता के रूप में कर सकते हैं, जो स्क्रिप्ट को इतना कुशल बनाता है।

चलो देखते हैं। चूंकि nबिट्स के साथ , हम मान 2 एन -1 तक का प्रतिनिधित्व कर सकते हैं , फिर nएक मनमाना मूल्य xका प्रतिनिधित्व करने के लिए आवश्यक बिट्स की संख्या छत है (लॉग 2 (x + 1))। तो, हमें आधार के लिए एक लघुगणक की छत की गणना करने के लिए एक फ़ंक्शन की आवश्यकता है। यह बल्कि आत्म-व्याख्यात्मक है:

log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

हमें स्थिति की आवश्यकता है n>0ताकि अगर यह बहुत बड़ा हो जाए, चारों ओर लपेटता है और नकारात्मक हो जाता है, तो लूप को समाप्त करने की गारंटी दी जाती है।

चरण 2: एक यादृच्छिक बिटस्ट्रीम का नमूना लें n

सबसे पोर्टेबल विचार या तो उपयोग करने के लिए हैं /dev/urandom(या यहां तक ​​कि /dev/randomअगर कोई मजबूत कारण है) या बैश के अंतर्निर्मित $RANDOMचर। आइए देखें कि इसे $RANDOMपहले कैसे करना है ।

विकल्प A: उपयोग करना $RANDOM

यह एलिया कगन द्वारा वर्णित विचार का उपयोग करता है । मूल रूप से, $RANDOM15-बिट पूर्णांक के नमूने के बाद से , हम $((RANDOM<<15|RANDOM))30-बिट पूर्णांक के नमूने का उपयोग कर सकते हैं । इसका मतलब है, $RANDOM15 बिट्स के पहले इन्वोकेशन को बाईं ओर शिफ्ट करें , और एक बिटवाइज़ या दूसरे $RANDOMइंवोकेशन के साथ प्रभावी रूप से दो स्वतंत्र रूप से सैंपल किए गए बिटस्ट्रिंग्स (या कम से कम बैश के अंतर्निहित के रूप में स्वतंत्र रूप से स्वतंत्र $RANDOM) को लागू करें।

45-बिट या 60-बिट पूर्णांक प्राप्त करने के लिए हम इसे दोहरा सकते हैं। इसके बाद बैश इसे संभाल नहीं सकते, लेकिन इसका मतलब है कि हम आसानी से 0 और 2 60 -1 के बीच एक यादृच्छिक मूल्य का नमूना ले सकते हैं । इसलिए, एक एन-बिट पूर्णांक का नमूना करने के लिए, हम प्रक्रिया को तब तक दोहराते हैं जब तक कि हमारी यादृच्छिक बिटस्ट्रिंग, जिसकी लंबाई 15-बिट चरणों में बढ़ती है, की लंबाई एन की तुलना में अधिक या बराबर होती है। अंत में, हम उन बिट्स को काट देते हैं जो सही तरीके से शिफ्ट करने के लिए उचित रूप से बिटवाइज़ द्वारा बहुत अधिक हैं, और हम एन-बिट यादृच्छिक पूर्णांक के साथ समाप्त होते हैं।

get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

विकल्प बी: का उपयोग करना /dev/urandom

वैकल्पिक रूप से, हम n-बिट पूर्णांक का उपयोग odऔर उपयोग कर सकते हैं /dev/urandomodबाइट्स पढ़ेंगे, अर्थात, लंबाई के बिटस्ट्रिंग्स 8. इसी तरह पिछले पद्धति में, हम सिर्फ इतने बाइट्स का नमूना लेते हैं कि समतुल्य बिट्स के बराबर संख्या n से अधिक या बराबर होती है, और उन बिट्स को काट दें जो बहुत अधिक हैं।

कम से कम n बिट प्राप्त करने के लिए आवश्यक बाइट्स की सबसे कम संख्या 8 की सबसे कम संख्या है जो n, अर्थात, (n + 7) / 8) से अधिक या बराबर है।

यह केवल 56-बिट पूर्णांक तक काम करता है। एक और बाइट का नमूना लेने से हमें 64-बिट पूर्णांक मिलेगा, अर्थात, 2 64 -1 तक का मूल्य , जो बैश संभाल नहीं सकता है।

get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

टुकड़ों को एक साथ रखना: मनमाने ढंग से श्रेणियों में यादृच्छिक पूर्णांक प्राप्त करें

हम- nबिट बिटस्ट्रिंग्स को अब नमूना कर सकते हैं, लेकिन हम पूर्ण रूप से यादृच्छिक रूप से , जहां 0से max, एक सीमा में पूर्णांकों को नमूना करना चाहते हैंmax जरूरी नहीं कि दोनों में से एक शक्ति मनमाने ढंग से हो सकता है। (हम पूर्वाग्रह का उपयोग नहीं कर सकते क्योंकि यह एक पूर्वाग्रह पैदा करता है।)

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

rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

चीजों को लपेटना

अंत में, हम पूर्णांक के बीच minऔर max, जहां minऔरmax यहां तक ​​कि मनमाने ढंग से नकारात्मक भी हो सकते हैं । जैसा कि पहले उल्लेख किया गया है, यह अब तुच्छ है।

चलो यह सब एक बैश स्क्रिप्ट में डाल दिया। सामान को पार्स करने के लिए कुछ तर्क करें ... हम दो तर्क चाहते हैं minऔर max, केवल एक तर्क max, जहां minचूक है 0

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

... और, अंत में, समान रूप से यादृच्छिक मान के बीच minका maxनमूना लेने के लिए, हम 0और के पूर्ण मान के बीच एक यादृच्छिक पूर्णांक का नमूना लेते हैं max-min, और minअंतिम परिणाम में जोड़ते हैं । :-)

diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

से प्रेरित होकर इस , मैं उपयोग करने के लिए कोशिश कर सकते हैं dieharder परीक्षण और बेंचमार्क इस PRNG के लिए, और यहाँ में मेरी निष्कर्ष डाल दिया। :-)


आपका समाधान मानता है कि sizeof(int) == 8(64 बिट) की वजह से--format=u
जवानों

1
आपका समाधान मुझे याद दिलाता है कि यादृच्छिक संख्या कैसे लिखी जाती है। random.Randomवर्ग 53 बिट का उपयोग करता है? जनरेटर बड़े पैमाने पर यादृच्छिक संख्या (कई इनवोकेशन) को वापस करने के लिए, random.SystemRandomउसी का उपयोग करता है जिसे उपयोग करके os.urandom()लागू किया जा सकता है /dev/urandom
jfs 14

uL का अर्थ है आकार (लंबा)> = 8 सीमा के लिए। इसकी गारंटी नहीं है। आप यह सुनिश्चित करने के लिए u8 का उपयोग कर सकते हैं कि प्लेटफ़ॉर्म में ऐसा पूर्णांक है।
jfs

@JFSebastian मैं सोच रहा था कि अभी तक मेरी स्क्रिप्ट एक लंबे इंट के आकार के बारे में किसी भी धारणा को हार्ड-कोड नहीं करती है। संभावित रूप से, यह तब भी काम करेगा जब एक लंबे हस्ताक्षरित इंट का आकार 64 बिट्स, जैसे, 128 बिट्स से अधिक (या कम) था। हालांकि, अगर मैं उपयोग करता --format=u8हूं तो मैं धारणा को हार्डकोड करता हूं sizeof(int)==8। दूसरी ओर, अगर उपयोग में --format=uLकोई समस्या नहीं है: मुझे नहीं लगता कि एक ऐसा प्लेटफॉर्म है जिसमें 64-बिट पूर्णांक हैं, लेकिन फिर भी लंबे इन्टेस को कुछ कम के रूप में परिभाषित करता है। इसलिए मूल रूप से मैं तर्क --format=uLदेता हूं कि अधिक लचीलेपन की अनुमति देता है। आपके क्या विचार हैं?
माल्टे स्कोरुप्पा सिप

वहाँ है long longकि 64 बिट हो सकता है, जबकि पूर्णांक = लंबे = कुछ प्लेटफॉर्म पर 32 बिट। यदि आप सभी प्लेटफार्मों पर इसकी गारंटी नहीं दे सकते तो आपको 0..2 ** 60 रेंज का दावा नहीं करना चाहिए। दूसरी तरफ इस तरह के प्लेटफॉर्म पर bash खुद इस रेंज का समर्थन नहीं कर सकता है (मुझे नहीं पता, शायद यह maxint_t का उपयोग करता है और फिर u8 अधिक सही है यदि आप तय सीमा का दावा करना चाहते हैं ( odयदि आपकी सीमा है तो अधिकतम निर्दिष्ट करने का समर्थन नहीं करता है) बैश का प्लेटफ़ॉर्म-डिपेंडेंट? रेंज है;)। यदि बैश रेंज साइज़ोफ़ लॉन्ग पर निर्भर करता है तो यूएल अधिक उपयुक्त हो सकता है)। क्या आप पूरी रेंज चाहते हैं जो सभी ओएस या एक निश्चित सीमा पर बैश का समर्थन करता है?
jfs

6

क्या यह zsh हो सकता है?

max=1000
integer rnd=$(( $(( rand48() )) * $max ))

आप बीज के साथ भी उपयोग करना चाह सकते हैं rand48(seed)। देखें man zshmodulesऔर man 3 erand48यदि आवश्यक हो तो विस्तृत विवरण के लिए।


मैं व्यक्तिगत रूप से zsh का उपयोग नहीं करता हूं, लेकिन यह एक बढ़िया अतिरिक्त है :)
माल्टे स्कोर्पुपा


5

यदि आप 0 से एक संख्या चाहते हैं (2 ^ n) -1 जहां n mod 8 = 0 तो आप बस n / 8 बाइट्स प्राप्त कर सकते हैं /dev/random। उदाहरण के लिए, एक यादृच्छिक का दशमलव प्रतिनिधित्व प्राप्त करने के लिए int:

od --read-bytes=4 --address-radix=n --format=u4 /dev/random | awk '{print $1}'

यदि आप सिर्फ n बिट्स लेना चाहते हैं तो आप सबसे पहले छत (n / 8) बाइट ले सकते हैं और अपनी इच्छित राशि पर सही बदलाव कर सकते हैं। उदाहरण के लिए यदि आप 15 बिट्स चाहते हैं:

echo $(($(od --read-bytes=2 --address-radix=n --format=u4 /dev/random | awk '{print $1}') >> 1))

यदि आप पूरी तरह से सुनिश्चित हैं कि आप यादृच्छिकता की गुणवत्ता के बारे में परवाह नहीं करते हैं और आप न्यूनतम रन समय की गारंटी देना चाहते हैं तो आप /dev/urandomइसके बजाय उपयोग कर सकते हैं /dev/random। सुनिश्चित करें कि आप जानते हैं कि आप उपयोग करने से पहले क्या कर रहे हैं /dev/urandom!


धन्यवाद। तो, से nयादृच्छिक यादृच्छिक बाइट्स प्राप्त करें /dev/urandomऔर उपयोग करें odइस उत्तर के रूप में आत्मा में भी ऐसा ही है । दोनों समान रूप से अच्छे हैं :) हालांकि दोनों को 0 ^ (n * 8) -1 बिट्स के माध्यम से 0 की एक निश्चित सीमा होने का नुकसान है, जहां n बाइट्स की संख्या है। मैं एक के लिए एक विधि पसंद करेंगे मनमाने ढंग से सीमा, अप करने के लिए 2 ^ 32-1, लेकिन यह भी कुछ भी कम। यह पूर्वाग्रह कठिनाई पैदा करता है।
माल्ट स्कोर्पुपा

के /dev/urandomबजाय उपयोग करने के लिए संपादित /dev/random- मैं उपयोग करने का कोई कारण नहीं देखता /dev/random, और यह वास्तव में महंगा / धीमा हो सकता है, या सिस्टम के अन्य भागों को धीमा कर सकता है। (बेझिझक वापस संपादित करें और समझाएं कि क्या वास्तव में इसकी आवश्यकता है।)
वोल्कर सेगेल

इसका ठीक उल्टा होना चाहिए: जब तक आपको पता है कि आपको / देव / यादृच्छिक की आवश्यकता है, तब तक उपयोग करें । यह मान लेना गलत है कि /dev/urandomपरिणाम इतने बदतर हैं /dev/randomकि ज्यादातर मामलों में यूरेनियम का उपयोग करने योग्य नहीं है । एक बार /dev/urandomप्रारंभिक (सिस्टम की शुरुआत में); /dev/randomलिनक्स पर लगभग सभी अनुप्रयोगों के लिए इसके परिणाम उतने ही अच्छे हैं । कुछ प्रणालियों पर यादृच्छिक और उरुग्वे समान होते हैं।
9

1
--format=uके साथ प्रतिस्थापित किया जाना चाहिए --format=u4क्योंकि सिद्धांत sizeof(int)से कम हो सकता 4है।
jfs

@ जेएफएसबेस्टियन इस पेपर में इस विषय पर बहुत ही रोचक चर्चा है। उनके निष्कर्ष दोनों कि प्रतीत हो रहा है /dev/randomऔर /dev/urandomअसंतोषजनक है, और है कि "लिनक्स एक सुरक्षित RNG जोड़ने चाहिए कि यह जब तक ब्लॉक पर्याप्त बीज एन्ट्रापी एकत्र किया है और उसके बाद की तरह बर्ताव करता urandom।"
l0b0

3

यह मानते हुए कि आपको बाहरी उपकरणों का उपयोग करने में कोई आपत्ति नहीं है, यह आपकी आवश्यकताओं को पूरा करना चाहिए:

rand=$(perl -e 'print int(rand(2**32-1))'); 

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


यह बड़ी संख्या के लिए टूट जाता है , 5 ** 1234
jfs

1
@JFSebastian हाँ यह करता है। मैंने इसे ओपी निर्दिष्ट के बाद से पोस्ट किया है 1^32-1लेकिन आपको इसे बड़ी संख्या के लिए ट्विक करने की आवश्यकता है।
terdon

2

आपको अपने वांछित अधिकतम की तुलना में निकटतम (2 ^ X) -1 बराबर या ग्रेटर मिलना चाहिए और बिट्स की संख्या प्राप्त करनी चाहिए। फिर बस कई बार कॉल / देव / रैंडम करें और सभी बिट्स को एक साथ जोड़ दें, जब तक आपके पास पर्याप्त न हो, सभी बिट्स को अलग कर दें जो बहुत अधिक हैं। यदि परिणामी संख्या आपके अधिकतम दोहराने से बड़ी है। सबसे खराब स्थिति में आपके पास अपने अधिकतम के नीचे एक यादृच्छिक संख्या प्राप्त करने का 50% से अधिक मौका है (इस सबसे खराब स्थिति के लिए) आप औसतन दो कॉल लेंगे।


यह वास्तव में दक्षता में सुधार करने के लिए एक बहुत अच्छा विचार है। रमेश के उत्तर और l0b0 के उत्तर दोनों को मूल रूप से यादृच्छिक बिट्स मिलते हैं /dev/urandom, लेकिन दोनों उत्तरों में यह हमेशा 8 बिट्स का गुणक होता है। बिट्स को ट्रंक करना, जो दशमलव के साथ स्वरूपण करने से पहले कम रेंज के लिए बहुत अधिक है od, दक्षता में सुधार करने के लिए एक अच्छा विचार है, क्योंकि लूप में केवल 2 पुनरावृत्तियों की अपेक्षित संख्या है, जैसा कि आप अच्छी तरह से समझाते हैं। यह, उल्लेखित उत्तरों में से किसी एक के साथ संयुक्त है, शायद जाने का रास्ता है।
माल्टे स्कॉरप्पा

0

आपका जवाब दिलचस्प है लेकिन काफी लंबा है।

यदि आप मनमाने ढंग से बड़ी संख्या चाहते हैं, तो आप एक सहायक में कई यादृच्छिक संख्याओं में शामिल हो सकते हैं:

# $1 - number of 'digits' of size base
function random_helper()
{
  base=32768
  random=0
  for((i=0; i<$1; ++i)); do
    let "random+=$RANDOM*($base**$i)"
  done
  echo $random
}

यदि समस्या पूर्वाग्रह है, तो इसे दूर करें।

# $1 - min value wanted
# $2 - max value wanted
function random()
{
  MAX=32767
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$RANDOM
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}

इन कार्यों को एक साथ जोड़ना

# $1 - min value wanted
# $2 - max value wanted
# $3 - number of 'digits' of size base
function random()
{
  base=32768
  MAX=$((base**$3-1))
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$(random_helper)
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.