एक बैश स्क्रिप्ट में एक सीमा से यादृच्छिक संख्या


197

मुझे 2000-65000शेल स्क्रिप्ट से बीच में एक यादृच्छिक पोर्ट नंबर उत्पन्न करना होगा । समस्या $RANDOMएक 15-बिट संख्या है, इसलिए मैं फंस गया हूँ!

PORT=$(($RANDOM%63000+2001)) यदि यह आकार सीमा के लिए नहीं था तो अच्छी तरह से काम करेगा।

क्या किसी के पास इस बात का उदाहरण है कि मैं यह कैसे कर सकता हूं, हो सकता है कि किसी चीज को /dev/urandomकिसी सीमा से निकालकर।

जवाबों:


398
shuf -i 2000-65000 -n 1

का आनंद लें!

संपादित करें : सीमा समावेशी है।


7
मुझे लगता shufहै कि अपेक्षाकृत हाल ही में - मैंने पिछले कुछ वर्षों में इसे उबंटू सिस्टम पर देखा है, लेकिन वर्तमान आरएचईएल / सेंटोस नहीं।
Cascabel

5
इसके अलावा, यह शायद इस उपयोग के लिए ठीक है, लेकिन मेरा मानना shufहै कि वास्तव में पूरे इनपुट को अनुमति देता है। यदि आप यादृच्छिक संख्या को बहुत बार उत्पन्न कर रहे हैं तो यह एक बुरा विकल्प है।
कैस्केबेल

3
@Jefromi: अपने सिस्टम पर, इस परीक्षण का उपयोग time for i in {1..1000}; do shuf -i 0-$end -n 1000 > /dev/null; doneऔर की तुलना end=1करने के लिए end=65535कम रेंज जो एक लाख पुनरावृत्तियों सेकंड से अधिक का अंतर 4 के बारे में करने के लिए राशि के लिए एक 25% सुधार के बारे में पता चला है। और यह ओपी बैश गणना एक मिलियन बार प्रदर्शन करने की तुलना में बहुत तेज है।
अगली सूचना तक रोक दिया गया।

9
@ डेनिस विलियमसन: अपने परीक्षण को -n 1नगण्य समय के अंतर के साथ, यहां तक ​​कि साथ चलाना end=4000000000। जानने के लिए अच्छा shufकाम करता है स्मार्ट, कठिन नहीं :-)
leedm777

6
मैं अपने मैक पर shuf नहीं है :(
वीरेन

79

Mac OS X और FreeBSD पर आप jot का उपयोग कर सकते हैं:

jot -r 1  2000 65000

5
इस उदाहरण में, jotअंतराल के न्यूनतम और अधिकतम (यानी, 2000 और 65000) के लिए अनुचित वितरण है। दूसरे शब्दों में, न्यूनतम और अधिकतम बार-बार उत्पन्न होंगे। विवरण और वर्कअराउंड के लिए मेरा जोट उत्तर देखें ।
बजे क्लिंट पच्ल

jotअधिकांश GNU / Linux वितरण में भी उपलब्ध है
Thor

43

बैश मैन पेज के अनुसार, $RANDOM0 और 32767 के बीच वितरित किया जाता है; अर्थात्, यह एक अहस्ताक्षरित 15-बिट मान है। मान लिया गया है $RANDOMकि समान रूप से वितरित किया गया है, तो आप समान रूप से वितरित 30-बिट पूर्णांक वितरित कर सकते हैं:

$(((RANDOM<<15)|RANDOM))

चूंकि आपकी सीमा 2 की शक्ति नहीं है, एक साधारण मोड्यूलो ऑपरेशन आपको लगभग एक समान वितरण देगा, लेकिन 30-बिट इनपुट रेंज और कम-से-16-बिट आउटपुट रेंज के साथ, जैसा कि आपके मामले में है, यह वास्तव में पर्याप्त होना चाहिए:

PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 ))

1
चर $RANDOMहमेशा सभी गोले में उपलब्ध नहीं है। एक अन्य समाधान की तलाश में
लुकस लाइसिस

अगर मैं इसे सही ढंग से समझ रहा हूं, तो आप 1,000,000,000 की सीमा के बीच 32,000 नंबर फैला रहे हैं। लेकिन वे केवल 2 ^ 15 के गुणकों पर हिट करेंगे - आप 2 ^ 15 तक स्किप-गिनती कर रहे हैं, 1 और 2 ^ 30 के बीच सभी अंकों को भरना समान रूप से नहीं है, जो कि एक समान वितरण है।
isomorphismes

@isomorphismes ध्यान दें कि कोड $RANDOMदो बार संदर्भित करता है । समर्थन करने वाले गोले पर $RANDOM, संदर्भित होने पर हर बार एक नया मान उत्पन्न होता है। तो यह कोड बिट्स को एक $RANDOMमान के साथ 14 के माध्यम से भरता है और बिट्स के 15 को दूसरे के साथ 29 से भरता है। मान लेना $RANDOMएक समान और स्वतंत्र है, यह 0 से 2 ** 30-1 के माध्यम से सभी मानों को बिना किसी लंघन के शामिल करता है।
जेसिन

41

और यहाँ अजगर के साथ एक है

randport=$(python -S -c "import random; print random.randrange(2000,63000)")

और एक जाग के साथ

awk 'BEGIN{srand();print int(rand()*(63000-2000))+2000 }'

6
यह एक मुझ से ऊपर उठता है। मैं विभिन्न प्रणालियों के लिए बैश स्क्रिप्ट लिखता हूं और मेरा मानना ​​है कि awk शायद नौकरी के लिए सबसे प्रचुर उपकरण है। एक मुद्दे के बिना मैक ओएस एक्स और सेंटो पर काम किया है और मुझे पता है कि यह मेरे डेबियन मशीन पर भी काम करेगा, और शायद कोई अन्य सामान्य-ईश * निक्स मशीन।
जॉन हंट

6
हालाँकि, awk का यादृच्छिक बीज केवल एक बार / सेकंड ताज़ा करने के लिए लगता है इसलिए आप चाहते हैं कि) सभी लागतों से बचें या बी) बीज को फिर से शुरू करें।
जॉन हंट

+1 क्योंकि यह संकलन के बिना केवल POSIX संभावना है: RANDOMPOSIX द्वारा गारंटीकृत नहीं है,
Ciro Santilli 病 be be be

-Sविकल्प के उपयोग से परिणाम प्राप्त होता है ImportError: No module named random। काम करता है अगर मैं इसे हटा दें। यकीन नहीं हो रहा था कि उसके लिए घोस्टडॉग का इरादा क्या था।
क्रिस जॉनसन

1
python -S -c "import random; print random.randrange(2000,63000)"ठीक काम करने लगता है। हालाँकि, जब मैं 1 और 2 के बीच एक यादृच्छिक संख्या प्राप्त करने की कोशिश करता हूं, तो मुझे हमेशा 1 लगता है ... विचार?
ह्यूबर्ट लेविल गौविन

17

सबसे सरल सामान्य तरीका जो मन में आता है वह है एक पर्ल वन-लाइनर:

perl -e 'print int(rand(65000-2000)) + 2000'

आप हमेशा दो नंबर का उपयोग कर सकते हैं:

PORT=$(($RANDOM + ($RANDOM % 2) * 32768))

आपको अभी भी अपनी सीमा पर रहना होगा। यह एक सामान्य एन-बिट यादृच्छिक संख्या विधि नहीं है, लेकिन यह आपके मामले के लिए काम करेगा, और यह सभी बैश के अंदर है।

यदि आप वास्तव में प्यारा होना चाहते हैं और / dev / urandom से पढ़ते हैं, तो आप ऐसा कर सकते हैं:

od -A n -N 2 -t u2 /dev/urandom

वह दो बाइट्स पढ़ेगा और उन्हें एक अहस्ताक्षरित इंट के रूप में प्रिंट करेगा; आपको अभी भी अपनी क्लिपिंग करनी है।


मैंने इस तकनीक और नोटिस का उपयोग किया कि अब और फिर कोई संख्या उत्पन्न नहीं होगी, बस रिक्त स्थान।
PdC

इसके लिए पर्ल की आवश्यकता होती है। मैं एक स्क्रिप्ट लिखता हूं, जो सभी लाइनक्स मशीनों पर नहीं awk
चलनी

कम या अधिक की कीमत पर रैंडम नंबरों को जोड़ने से मध्य परिणाम निकलते हैं। यह समान रूप से यादृच्छिक नहीं है।
isomorphismes

@isomorphismes हाँ, यदि आप सचमुच दो यादृच्छिक संख्याएँ जोड़ रहे हैं। लेकिन, यह मानते हुए कि आप यहां दूसरी अभिव्यक्ति का जिक्र कर रहे हैं, यह वही नहीं है जो यह कर रहा है। यह [0,32767] में एक यादृच्छिक संख्या है, साथ ही अगले बिट यानी 0 या 32768 के लिए एक स्वतंत्र यादृच्छिक विकल्प है। यह एक समान है। (यह मूल प्रश्न के लिए आदर्श नहीं है, क्योंकि आपको फिर से रेरोलिंग के साथ रेंज को क्लिप करना है।)
कास्कैबेल

7

यदि आप एक बैश विशेषज्ञ नहीं हैं और इसे लिनक्स-आधारित बैश स्क्रिप्ट में एक चर में लाना चाहते हैं, तो यह प्रयास करें:

VAR=$(shuf -i 200-700 -n 1)

यह आपको 200 से 700 तक $VAR, समावेशी बनाता है।


5

यहाँ एक और है। मैंने सोचा था कि यह किसी भी चीज़ के बारे में काम करेगा, लेकिन काम पर मेरे सेंटो बॉक्स पर सॉर्ट का यादृच्छिक विकल्प उपलब्ध नहीं है।

 seq 2000 65000 | sort -R | head -n 1

3
sort -ROS X पर उपलब्ध नहीं है।
LRI

5

$RANDOM0 और 32767 के बीच एक संख्या है। आप 2000 और 65000 के बीच एक पोर्ट चाहते हैं। ये 63001 संभावित पोर्ट हैं। यदि हम 2000 और 33500 के $RANDOM + 2000बीच के मूल्यों से चिपके रहते हैं , तो हम 31501 पोर्ट की एक सीमा को कवर करते हैं। यदि हम एक सिक्का फ्लिप करते हैं और फिर सशर्त रूप से परिणाम में 31501 जोड़ते हैं, तो हम 33501 से 65001 तक अधिक पोर्ट प्राप्त कर सकते हैं । फिर अगर हम सिर्फ 65001 को छोड़ते हैं, तो हमें सभी बंदरगाहों के लिए एक समान संभाव्यता वितरण के साथ आवश्यक सटीक कवरेज मिलती है, ऐसा लगता है।

random-port() {
    while [[ not != found ]]; do
        # 2000..33500
        port=$((RANDOM + 2000))
        while [[ $port -gt 33500 ]]; do
            port=$((RANDOM + 2000))
        done

        # 2000..65001
        [[ $((RANDOM % 2)) = 0 ]] && port=$((port + 31501)) 

        # 2000..65000
        [[ $port = 65001 ]] && continue
        echo $port
        break
    done
}

परिक्षण

i=0
while true; do
    i=$((i + 1))
    printf "\rIteration $i..."
    printf "%05d\n" $(random-port) >> ports.txt
done

# Then later we check the distribution
sort ports.txt | uniq -c | sort -r

5

तुम यह केर सकते हो

cat /dev/urandom|od -N2 -An -i|awk -v f=2000 -v r=65000 '{printf "%i\n", f + r * $1 / 65536}'

यदि आपको अधिक जानकारी की आवश्यकता है, तो शेल स्क्रिप्ट रैंडम नंबर जेनरेटर देखें


लगभग। यह आपको 2000 से 67000 तक की सीमा प्रदान करता है।
ओगरे Psalm33


3

बैश प्रलेखन कहता है कि हर बार $RANDOMसंदर्भित किया जाता है, 0 और 32767 के बीच एक यादृच्छिक संख्या वापस आ जाती है। यदि हम लगातार दो संदर्भों को जोड़ते हैं, तो हमें 0 से 65534 तक मान मिलते हैं, जो 2000 और 65000 के बीच यादृच्छिक संख्या के लिए 63001 संभावनाओं की वांछित सीमा को कवर करता है।

इसे सटीक सीमा तक समायोजित करने के लिए, हम modulo 63001 का उपयोग करते हैं, जो हमें 0 से 63000 तक का मूल्य देगा। इसके बदले में 2000 और 65000 के बीच वांछित यादृच्छिक संख्या प्रदान करने के लिए 2000 की वृद्धि की आवश्यकता है। यह हो सकता है संक्षेप में इस प्रकार है:

port=$((((RANDOM + RANDOM) % 63001) + 2000))

परिक्षण

# Generate random numbers and print the lowest and greatest found
test-random-max-min() {
    max=2000
    min=65000
    for i in {1..10000}; do
        port=$((((RANDOM + RANDOM) % 63001) + 2000))
        echo -en "\r$port"
        [[ "$port" -gt "$max" ]] && max="$port"
        [[ "$port" -lt "$min" ]] && min="$port"
    done
    echo -e "\rMax: $max, min: $min"
}

# Sample output
# Max: 64990, min: 2002
# Max: 65000, min: 2004
# Max: 64970, min: 2000

गणना की शुद्धता

गणना की शुद्धता के लिए एक पूर्ण, पाशविक बल परीक्षण है। यह कार्यक्रम परीक्षण के तहत गणना का उपयोग करके, सभी 63001 विभिन्न संभावनाओं को यादृच्छिक रूप से उत्पन्न करने की कोशिश करता है। --jobsपैरामीटर यह तेजी से चलाने बनाना चाहिए, लेकिन यह (उत्पन्न 63,001 से कम हो सकती संभावनाओं की कुल) निर्धारित करने योग्य नहीं है।

test-all() {
    start=$(date +%s)
    find_start=$(date +%s)
    total=0; ports=(); i=0
    rm -f ports/ports.* ports.*
    mkdir -p ports
    while [[ "$total" -lt "$2" && "$all_found" != "yes" ]]; do
        port=$((((RANDOM + RANDOM) % 63001) + 2000)); i=$((i+1))
        if [[ -z "${ports[port]}" ]]; then
            ports["$port"]="$port"
            total=$((total + 1))
            if [[ $((total % 1000)) == 0 ]]; then
                echo -en "Elapsed time: $(($(date +%s) - find_start))s \t"
                echo -e "Found: $port \t\t Total: $total\tIteration: $i"
                find_start=$(date +%s)
            fi
        fi
    done
    all_found="yes"
    echo "Job $1 finished after $i iterations in $(($(date +%s) - start))s."
    out="ports.$1.txt"
    [[ "$1" != "0" ]] && out="ports/$out"
    echo "${ports[@]}" > "$out"
}

say-total() {
    generated_ports=$(cat "$@" | tr ' ' '\n' | \sed -E s/'^([0-9]{4})$'/'0\1'/)
    echo "Total generated: $(echo "$generated_ports" | sort | uniq | wc -l)."
}
total-single() { say-total "ports.0.txt"; }
total-jobs() { say-total "ports/"*; }
all_found="no"
[[ "$1" != "--jobs" ]] && test-all 0 63001 && total-single && exit
for i in {1..1000}; do test-all "$i" 40000 & sleep 1; done && wait && total-jobs

यह निर्धारित करने के लिए कि p/qसभी 63001 संभावनाओं को प्राप्त करने के लिए कितने पुनरावृत्तियों की आवश्यकता है , मुझे लगता है कि हम नीचे दिए गए अभिव्यक्ति का उपयोग कर सकते हैं। उदाहरण के लिए, यहां 1/2 से अधिक संभावना के लिए गणना की जाती है , और यहां 9/10 से अधिक के लिए

अभिव्यक्ति


1
तुम गलत हो। $RANDOMहै एक पूर्णांक । आपकी "चाल" के साथ कई मूल्य हैं जो कभी भी प्राप्त नहीं होंगे। -1
gnourf_gniourf

2
मुझे यकीन नहीं है कि आप "एक पूर्णांक" के साथ क्या मतलब है, लेकिन सही है, एल्गोरिथ्म गलत था। सीमित सीमा से यादृच्छिक मान गुणा करने से सीमा नहीं बढ़ेगी। हमें $RANDOMइसके बजाय दो एक्सेस की राशि की आवश्यकता है , और इसे $RANDOMहर दो पर बदलने के लिए माना जाता है कि दो से गुणा करने के लिए प्रतिक्षेपक नहीं है । मैंने उत्तर को वर्जन संस्करण के साथ अपडेट किया है।

6
ऐसा करने RANDOM+RANDOMसे आपको 0 और 65534 के बीच यादृच्छिक संख्याओं का एक समान वितरण नहीं मिलेगा।
gniourf_gniourf

3
सही, दूसरे शब्दों में, सभी रकमों को होने का समान मौका नहीं है। वास्तव में, यह उस से गोज़ है, अगर हम ग्राफ की जांच करते हैं तो यह पिरामिड है! मुझे लगता है कि यही कारण है कि मैं ऊपर दिए गए फॉर्मूले की अपेक्षा से बहुत अधिक गणना समय प्राप्त कर रहा हूं। मॉडुलो ऑपरेशन के साथ एक समस्या यह भी है: 63001 से (32767 + 32767) तक की रकम बाकी बंदरगाहों की तुलना में पहले 2534 बंदरगाहों के लिए होने की संभावना दोगुनी है। मैं विकल्पों के बारे में सोच रहा हूं, लेकिन मुझे लगता है कि एक नए उत्तर के साथ खरोंच से शुरू करना बेहतर है, इसलिए मैं इसे हटाने के लिए वोट कर रहा हूं।

4
यह 2 छह-पक्षीय पासा को रोल करने जैसा है। सांख्यिकीय रूप से यह आपको एक घंटी वक्र देता है: "2" या "12" को रोल करने की कम संभावना, बीच में "7" प्राप्त करने की उच्चतम संभावना।
ओगरे Psalm33


2

PORT=$(($RANDOM%63000+2001)) आप जो चाहते हैं, उसके करीब हूं।

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))आकार सीमा के आसपास हो जाता है जो आपको परेशान करता है। चूंकि बैश एक संख्या चर और एक स्ट्रिंग चर के बीच कोई अंतर नहीं करता है, यह पूरी तरह से अच्छी तरह से काम करता है। "संख्या" $RANDOMको एक स्ट्रिंग की तरह समेटा जा सकता है, और फिर गणना में संख्या के रूप में उपयोग किया जाता है। गजब का!


1
मैं देख रहा हूं कि तुम क्या कह रहे हो। मैं मानता हूं कि वितरण अलग होगा, लेकिन आपको वास्तविक यादृच्छिकता वैसे भी नहीं मिल सकती है। कभी-कभी $ RANDOM का उपयोग करना बेहतर हो सकता है, कभी-कभी $ RANDOM $ RANDOM का, और कभी-कभी $ RANDOM $ RANDOM $ RANDOM का और भी अधिक वितरण पाने के लिए। अधिक $ रैंडम उच्च बंदरगाह संख्याओं के पक्षधर हैं, जहां तक ​​मैं बता सकता हूं।
ख़राबी

(मैंने अपनी मूल टिप्पणी हटा दी, क्योंकि मैंने कुछ गलत संख्यात्मक मानों का उपयोग किया था और टिप्पणी को संपादित करने में बहुत देर हो चुकी थी)। सही। x=$(( $n%63000 )के समान है x=$(( $n % 65535 )); if [ $x -gt 63000 ]; then x=63000
चेपनर

मैं गणित की आलोचना नहीं कर रहा था (या कर भी रहा था)। मैंने बस इसे स्वीकार कर लिया। यह मेरा मतलब है: num = ($ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM); लेने = $ (($ यादृच्छिक% 3)); PORT = $ (($ {num [$ pick]}% 63000 + 2001)) --- यह बहुत परेशानी की तरह लगता है ...
Wastrel

1

आप के माध्यम से यादृच्छिक संख्या प्राप्त कर सकते हैं urandom

head -200 /dev/urandom | cksum

आउटपुट:

3310670062 52870

उपरोक्त संख्या के एक भाग को पुनः प्राप्त करने के लिए।

head -200 /dev/urandom | cksum | cut -f1 -d " "

फिर आउटपुट है

3310670062

अपनी आवश्यकता को पूरा करने के लिए,

head -200 /dev/urandom |cksum | cut -f1 -d " " | awk '{print $1%63000+2001}'


0

यह है कि मैं आमतौर पर यादृच्छिक संख्या कैसे उत्पन्न करता हूं। फिर मैं "NUM_1" का उपयोग पोर्ट नंबर के लिए चर के रूप में करता हूं। यहाँ एक संक्षिप्त उदाहरण स्क्रिप्ट है।

#!/bin/bash

clear
echo 'Choose how many digits you want for port# (1-5)'
read PORT

NUM_1="$(tr -dc '0-9' </dev/urandom | head -c $PORT)"

echo "$NUM_1"

if [ "$PORT" -gt "5" ]
then
clear
echo -e "\x1b[31m Choose a number between 1 and 5! \x1b[0m"
sleep 3
clear
exit 0
fi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.