Bash Betiğindeki bir aralıktan rastgele sayı


198

2000-65000Bir kabuk komut dosyası arasında rasgele bir bağlantı noktası numarası oluşturmak gerekiyor . Sorun $RANDOM15 bitlik bir sayı, bu yüzden sıkışıp kaldım!

PORT=$(($RANDOM%63000+2001)) boyut sınırlaması olmasaydı güzel çalışırdı.

Herkes, belki bir şey ayıklamak /dev/urandomve bir aralık içinde alarak bunu nasıl bir örnek var mı ?

Yanıtlar:


398
shuf -i 2000-65000 -n 1

Zevk almak!

Düzenleme : Aralık dahil.


7
Bence shufnispeten yeni - son birkaç yıldır Ubuntu sistemlerinde gördüm ama mevcut RHEL / CentOS'da değil.
Cascabel

5
Ayrıca, bu kullanım için muhtemelen iyi, ama shufaslında tüm girdiye izin verdiğine inanıyorum . Rastgele sayıları çok sık oluşturuyorsanız, bu kötü bir seçimdir.
Cascabel

3
@Jefromi: Sistemimde, bu testi kullanarak time for i in {1..1000}; do shuf -i 0-$end -n 1000 > /dev/null; doneve karşılaştırmak end=1, end=65535bir milyon yinelemede yaklaşık 4 saniye fark olan daha kısa aralık için yaklaşık% 25'lik bir iyileşme gösterdi. Ve OP'nin Bash hesaplamasını bir milyon kez yapmaktan çok daha hızlı.
sonraki duyuruya kadar duraklatıldı.

9
@Dennis Williamson: Testinizi çalıştırırken -n 1bile ihmal edilebilir zaman farklılıkları gösterdi end=4000000000. Bilmek iyi shufçalışıyor akıllı, zor değil :-)
leedm777

6
Mac'imde shuf yok :(
Viren

79

Mac OS X ve FreeBSD'de jot da kullanabilirsiniz:

jot -r 1  2000 65000

5
Bu örnekte, jotaralığın minimum ve maksimum değerleri için adil olmayan bir dağılım vardır (yani, 2000 ve 65000). Başka bir deyişle, min ve maks daha az üretilir. Ayrıntılar ve geçici bir çözüm için notuma bakın .
Clint Pachl

jotçoğu GNU / Linux dağıtımında da mevcuttur
Thor

43

Bash man sayfasına göre, $RANDOM0 ile 32767 arasında dağıtılır; yani, işaretsiz 15 bitlik bir değerdir. $RANDOMTekdüze bir şekilde dağıtıldığı varsayılarak , aşağıdaki gibi tekdüze bir şekilde dağılmış işaretsiz 30 bitlik bir tam sayı oluşturabilirsiniz:

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

Aralığınız 2'nin gücü olmadığından, basit bir modulo işlemi size neredeyse tekdüze bir dağıtım sağlar, ancak 30 bit giriş aralığı ve 16 bit'ten daha düşük bir çıkış aralığıyla, durumunuzda olduğu gibi, bu gerçekten yeterince yakın olmalıdır:

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

1
değişken $RANDOMher kabukta her zaman mevcut değildir. Başka bir çözüm mi arıyorsunuz
Lukas Liesis

Bunu doğru anlıyorsam, 1.000.000.000 aralığında 32.000 sayı yayıyorsunuz. Ancak sadece 2 ^ 15'in katlarına çarpacaklar - 2 ^ 15 ile atlıyorsunuz, 1 ve 2 ^ 30 arasındaki tüm basamakları eşit olarak doldurmuyorsunuz, bu tekdüze bir dağılımdır.
izomorfizmalar

@isomorphismes Kodun $RANDOMiki kez başvurduğunu unutmayın . Desteklenen kabuklarda, $RANDOMher referans alındığında yeni bir değer oluşturulur. Böylece bu kod 0 ile 14 arasındaki bitleri bir $RANDOMdeğerle doldurur ve 15 ile 29 arasındaki bitleri başka bir değerle doldurur. $RANDOMTekdüze ve bağımsız olduğu varsayıldığında , hiçbir şey atlanmadan 0 ile 2 ** 30-1 arasındaki tüm değerleri kapsar.
Jesin

41

ve işte Python ile bir tane

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

ve biri awk ile

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

6
Bu benden bir oy aldı. Çeşitli sistemler için bash komut dosyaları yazıyorum ve awk'ın muhtemelen iş için en bol araç olduğuna inanıyorum. Sorunsuz bir şekilde mac os x ve centos üzerinde çalıştı ve debian makinemde ve muhtemelen herhangi bir normal-ish * nix makinesinde çalışacağını biliyorum.
John Hunt

6
Bununla birlikte, awk'ın rastgele tohumu sadece bir kez / saniye yenilenir gibi görünür;
John Hunt

+1 çünkü bu derleme olmadan tek POSIX olasılığı gibi görünüyor: RANDOMPOSIX tarafından garanti edilmez,
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

-SSeçeneği kullanmak sonuçlanır ImportError: No module named random. Bunu kaldırırsam çalışır. Bunun için hayaletlerin niyetinin ne olduğundan emin değilim.
Chris Johnson

1
python -S -c "import random; print random.randrange(2000,63000)"iyi çalışıyor gibi görünüyor. Ancak, 1 ile 2 arasında rastgele bir sayı elde etmeye çalıştığımda, her zaman 1 ... Düşünceler alıyorum gibi görünüyor?
Hubert Léveillé Gauvin

17

Akla gelen en basit genel yol, perl bir astardır:

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

Her zaman iki sayı kullanabilirsiniz:

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

Hala aralığınıza göre kırpmanız gerekiyor. Bu genel bir n-bit rasgele sayı yöntemi değil, ama sizin durumunuz için işe yarayacak ve hepsi bash içinde.

Gerçekten sevimli olmak ve / dev / urandom'dan okumak istiyorsanız, bunu yapabilirsiniz:

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

Bu iki baytı okur ve imzasız bir int olarak yazdırır; Kırpmayı hala yapmak zorundasın.


Bu tekniği kullandım ve ara sıra üretilen bir sayı olmayacağını, sadece boşluk olacağını fark ettim.
PdC

Yüklü perl gerektirir. Tüm linux makinelerinde olmasa da en çok çalışacak bir senaryo yazıyorum awk, başka bir cevabın versiyonuna
bağlıyım

Rastgele sayılar eklemek düşük veya yüksek pahasına orta sonuçları destekler. Düzgün rastgele değildir.
izomorfizmalar

@isomorphismes Evet, kelimenin tam anlamıyla iki rastgele sayı ekliyorsanız. Ancak, buradaki ikinci ifadeden bahsettiğinizi varsayarsak, yaptığı şey bu değildir. [0,32767] cinsinden rastgele bir sayı artı bir sonraki bit için bağımsız bir rastgele seçim, yani 0 veya 32768. Tekdüze. (Menüyü yeniden kaydırarak kırpmanız gerektiğinden orijinal soru için ideal değildir.)
Cascabel

7

Bir bash uzmanı değilseniz ve bunu Linux tabanlı bir bash betiğinde bir değişkene dönüştürmek istiyorsanız, şunu deneyin:

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

Bu size 200 ila 700 aralığında $VARdahil olur.


5

İşte burada bir başkası. Hemen hemen her şey üzerinde çalışacağını düşündüm, ancak sıralamadaki rastgele seçenek işyerindeki centos kutumda mevcut değil.

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

3
sort -ROS X'te de mevcut değildir.
Lri

5

$RANDOM0 ile 32767 arasında bir sayıdır. 2000 ile 65000 arasında bir bağlantı noktası istiyorsunuz. Bunlar 63001 olası bağlantı noktasıdır. 2000 ve 33500$RANDOM + 2000 arasındaki değerlere bağlı kalırsak, 31501 bağlantı noktasını kapsarız. Bir madeni para çevirir ve koşullu olarak sonuca 31501 eklersek , 33501'den 65001'e kadar daha fazla bağlantı noktası alabiliriz . Sonra sadece 65001'i düşürürsek, tam olarak gereken kapsama alanını alırız, tüm portlar için tekdüze bir olasılık dağılımı ile, görünüyor.

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
}

Test yapmak

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

Ruby ile aynı:

echo $(ruby -e 'puts rand(20..65)') #=> 65 (inclusive ending)
echo $(ruby -e 'puts rand(20...65)') #=> 37 (exclusive ending)

3

Bash belgelerine her $RANDOMgönderme yapıldığında 0 ile 32767 arasında rastgele bir sayı döndürüldüğünü söylüyor . İki ardışık referansı toplarsak, 0 ile 65534 arasında değerler alırız, bu değer 2000 ile 65000 arasında rastgele bir sayı için 63001 olasılık aralığını kapsar.

Tam aralığa ayarlamak için, bize 0 ila 63000 arasında bir değer verecek olan toplam modulo 63001'i kullanıyoruz. Bu da 2000 ve 65000 arasında istenen rasgele sayıyı sağlamak için 2000 yılına kadar bir artışa ihtiyaç duyuyor. aşağıdaki gibi özetlenmiştir:

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

Test yapmak

# 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

Hesaplamanın doğruluğu

İşte hesaplamanın doğruluğu için tam, kaba kuvvet testi. Bu program sadece test edilen hesaplamayı kullanarak 63001 farklı olasılıkları rastgele oluşturmaya çalışır. --jobsParametre daha hızlı çalışmasını sağlamak gerekir, ancak (63001 daha düşük olabilir oluşturulan olasılıklar toplamı) deterministik değil.

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/qOluşturulan 63001 olasılığının tümünün belirli bir olasılığını elde etmek için kaç yineleme gerektiğini belirlemek için , aşağıdaki ifadeyi kullanabileceğimize inanıyorum. Örneğin, burada 1/2'den büyük bir olasılık ve 9/10'dan büyük bir olasılık için hesaplama .

ifade


1
Yanılıyorsun. $RANDOMolan bir tam sayıdır . "Hile" ile asla ulaşılamayacak birçok değer vardır. -1.
gniourf_gniourf

2
"Tamsayıdır" ile ne demek istediğinizden emin değilim, ama doğru, algoritma yanlıştı. Sınırlı bir aralığından rastgele değeri çarpımı olacaktır değil aralığı arttırmaktadır. $RANDOMBunun yerine iki erişimi toplamamız $RANDOMgerekir ve her erişimde değişmesi gerektiğinden bunu ikiye çarparak yeniden düzenlememeliyiz . Cevabı toplam sürümüyle güncelledim.

6
Doing RANDOM+RANDOMsize vermeyecektir üniforma 0 ile 65534 arasında rasgele sayı dağılımını
gniourf_gniourf

3
Doğru, diğer bir deyişle, tüm meblağların eşit gerçekleşme şansı yoktur. Aslında, bundan osuruk, grafiği kontrol edersek bir piramit! Bu yüzden yukarıdaki formülde beklenenden çok daha büyük hesaplama süreleri elde ettiğimi düşünüyorum. Modulo işlemiyle ilgili bir sorun da var: 63001 ile (32767 + 32767) arasındaki toplamlar, bağlantı noktalarının geri kalanına kıyasla ilk 2534 bağlantı noktasının gerçekleşme olasılığını iki katına çıkarıyor. Alternatifleri düşünüyorum, ama yeni bir cevapla sıfırdan başlamak daha iyi, bu yüzden bunu silmek için oy kullanıyorum.

4
Altı altı zar atmak gibi. İstatistiksel olarak size bir çan eğrisi verir: ortada bir "7" elde etme olasılığı en yüksek olan "2" veya "12" yi döndürme olasılığı düşüktür.
Ogre Psalm33


2

PORT=$(($RANDOM%63000+2001)) istediğine yakın olduğunu düşünüyorum.

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))sizi rahatsız eden boyut sınırlamasını aşar. Bash, bir sayı değişkeni ile bir dize değişkeni arasında hiçbir ayrım yapmadığından, bu mükemmel bir şekilde çalışır. "Sayı" $RANDOMbir dize gibi birleştirilebilir ve daha sonra hesaplamada sayı olarak kullanılabilir. İnanılmaz!


1
Ne dediğini anlıyorum. Dağıtımın farklı olacağına katılıyorum, ama yine de gerçek bir rastgelelik elde edemezsiniz. Daha eşit bir dağıtım elde etmek için bazen $ RANDOM, bazen $ RANDOM $ RANDOM ve bazen $ RANDOM $ RANDOM $ RANDOM kullanmak daha iyi olabilir. Daha fazla $ RANDOM, anlayabildiğim kadarıyla daha yüksek port numaralarını tercih ediyor.
Wastrel

(Yanlış sayısal değerler kullandığım ve yorumu düzenlemek için çok geç olduğum için orijinal yorumumu sildim). Sağ. x=$(( $n%63000 )kabaca benzer x=$(( $n % 65535 )); if [ $x -gt 63000 ]; then x=63000.
chepner

Matematiği eleştirmeyecek, hatta yapmayacaktım. Ben sadece kabul ettim. Demek istediğim buydu: num = ($ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM); çekme = $ (($ RASGELE% 3)); PORT = $ (($ {num [$ pick]}% 63000 + 2001)) --- bu çok fazla sorun gibi görünüyor ...
Wastrel

1

Rastgele sayıyı urandom

head -200 /dev/urandom | cksum

Çıktı:

3310670062 52870

Yukarıdaki sayının bir kısmını almak için.

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

Sonra çıktı

3310670062

İhtiyacınızı karşılamak için,

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


0

Genellikle rasgele sayılar üretiyorum. Sonra kullandığım bağlantı noktası numarası için değişken olarak "NUM_1" kullanıyorum. İşte kısa bir örnek betik.

#!/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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.