Bash'de rastgele sayı nasıl oluşturulur?


236

Bash'te bir aralıkta rastgele bir sayı nasıl oluşturulur?


21
Ne kadar rastgele olması gerekiyor?
bdonlan

Yanıtlar:


283

Kullanın $RANDOM. Genellikle basit kabuk aritmetiği ile birlikte kullanışlıdır. Örneğin, 1 ile 10 arasında (dahil) rastgele bir sayı oluşturmak için:

$ echo $((1 + RANDOM % 10))
3

Gerçek jeneratör variables.cfonksiyonda brand(). Eski versiyonlar basit bir lineer jeneratördü. Sürüm 4.0, 1985 kağıdına atıfta bulunan bashbir jeneratör kullanıyor , bu muhtemelen sahte bir sayıların iyi bir kaynağı olduğu anlamına geliyor. Ben bir simülasyon için (ve kesinlikle kripto için) kullanmazdım, ama muhtemelen temel komut dosyası görevleri için yeterli.

Kullanabileceğiniz ciddi rasgele sayılar gerektiren bir şey yapıyorsanız /dev/randomveya /dev/urandomvarsa:

$ dd if=/dev/urandom count=4 bs=1 | od -t d

21
Burada dikkatli ol. Bu bir tutamda iyi olsa da, rastgele sayılar üzerinde aritmetik yapmak sonucunuzun rasgele olmasını önemli ölçüde etkileyebilir. durumunda $RANDOM % 10, 8 ve 9 da, eğer, ölçülebilir (gerçi sınırlı olarak) 0-7 den daha az olasıdır $RANDOMrastgele veri sağlam bir kaynağıdır.
dimo414

3
@ dimo414 "marjinal olarak" merak ediyorum, bunun hakkında daha fazla şey bulabileceğim bir kaynağın var mı?
PascalVKooten

58
Rastgele girdinizi modüle ederek sonuçları " güvercin delinir ". Yana $RANDOMbireyin aralıktır 0-32767numaraları 0- 7eşleme 3277farklı olası girişler, ancak 8ve 9ancak üretilebilir 3276farklı yollar (çünkü 32768ve 32769mümkün değildir). Bu, hızlı saldırılar için küçük bir sorundur, ancak sonucun düzgün bir şekilde rastgele olmadığı anlamına gelir. Java gibi rastgele kütüphaneler, Randombölünemez bir sayıyı değiştirmek yerine belirli aralıkta düzgün bir rastgele sayı döndürmek için işlevler sunar.
dimo414

1
@JFSebastian çok doğru - modulo ile ilgili sorun , sadece kötü PRNG'ler değil , herhangi bir RNG'nin tekdüzeliğini bozabilmesidir, ancak bunu çağırdığınız için teşekkürler.
dimo414

14
Sadece bağlam için,% 10, 8 ve 9 için temel pigeonholing, 0-7'den yaklaşık% 0,03 daha az olasıdır. Kabuk betiğiniz bundan daha doğru düzgün rasgele sayılar gerektiriyorsa, elbette daha karmaşık ve uygun bir mekanizma kullanın.
Nelson

70

Lütfen bakınız $RANDOM:

$RANDOM 0 - 32767 aralığındaki bir sahte tamsayı döndüren dahili bir Bash işlevidir (sabit değil). Bir şifreleme anahtarı oluşturmak için kullanılmamalıdır.


1
Mu 32767herhangi bir özel anlamı vardır?
Jin Kwon

14
@JinKwon 32767, 2^16 / 2 - 1işaretli bir 16 bit tam sayı için üst sınırdır.
Jeffrey Martinez

@ JinKwon neden olduğunu söylemiyor 2^15 - 1musun? Eşdeğer, bu yüzden sadece eksik olduğum bir bağlam olup olmadığını merak ediyorum?
Brett Holman

11
@BrettHolman Bence imzalı 16 bit tamsayı "imzalı" bölümünü işaret etmeye çalışıyordu. Pozitif ve negatif girişler için ikiye bölünmüş 2 ^ 16 değer.
cody

Bu cevap soruyu cevaplamıyor.
velet

48

Ayrıca shuf kullanabilirsiniz (coreutils'te bulunur).

shuf -i 1-100000 -n 1

Menzil sonu olarak değişkenleri nasıl geçirirsiniz? Bunu anladım:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
$varAralık sonu yerine şunu ekleyin :var=100 && shuf -i 1-${var} -n 1
knipwim

2
N rastgele sayılar oluşturmak kolay olduğu için bu seçeneği tercih ediyorum -n. Örneğin , 1 ile 100 arasında 5 rasgele sayı oluşturun :shuf -i 1-100 -n 5
aerijman

Anladığım kadarıyla sayılar rastgele değil. Eğer belirtirseniz shuf -i 1-10 -n 10size 1-10 exactl itibaren tüm numaraları alacak. Eğer belirtirseniz -n 15, sadece bu 10 rakamı tam olarak bir kez alırsınız. Bu gerçekten sadece karışıktır, rastgele sayılar üretmez.
radlan

Yedek ile rastgele sayılar almak için: -r
Geoffrey Anderson

36

Bunu kabuğunuzdan deneyin:

$ od -A n -t d -N 1 /dev/urandom

Burada, -t dçıktı biçiminin ondalık olarak imzalanması gerektiğini belirtir; -N 1bir bayt okumayı söylüyor /dev/urandom.


2
Soru bir aralıktaki sayıları soruyor.
JSycamore

9
boşlukları kaldırabilirsiniz:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

awk'den rastgele sayı da alabilirsiniz

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 biliyorsunuz, ilk başta neden böyle yapmak isteseydiniz, ama aslında çok beğendim.
zelanix

1
Tohumlama içeren bir çözüm verdiğiniz için teşekkürler. Hiçbir yerde bulamadım!
so.very.tired

2
Tohumlama için +1. srand()Tohumun mevcut CPU zamanı olduğunu belirtmekte fayda var . Belirli bir tohum belirtmeniz gerekiyorsa, RNG çoğaltılabilir, tohumun srand(x)nerede xolduğunu kullanın . Ayrıca, GNU awk'ın sayısal işlev kılavuzundan alıntılanan "farklı awk uygulamaları dahili olarak farklı rasgele sayı üreteçleri kullanır." Sonuç olarak, istatistiksel dağıtım oluşturmakla ilgileniyorsanız, bir çalışma zamanından diğerine farklı platformlarda (hepsi çalışıyor awkveya çalışıyor gawk) küçük değişiklikler beklemelisiniz .
Cbhihe

18

$ RANDOM var. Nasıl çalıştığını tam olarak bilmiyorum. Ama işe yarıyor. Test için şunları yapabilirsiniz:

echo $RANDOM

16

Bu numarayı seviyorum:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...


7
$ RANDOM 0 - 32767 aralığındadır. 1, 2 veya 3 ile başlayan 4-9'dan daha fazla sayı ile sonuçlanacaksınız. Dengesiz bir dağıtım konusunda iyiyseniz, bu işe yarayacaktır.
jbo5112

2
@ jbo5112 Tamamen haklısın, son rakamı göstermeye ne dersin? echo $ {RANDOM: 0-1} bir basamak için, $ {RANDOM: 0-2} iki basamak için ...?
fraff

4
Son rakamları kullanırsanız, oldukça iyi olacaktır, ancak 0'ları ve 00'ları içerecektir. Tek bir basamakta 0-7, 8-9'dan% 0.03 daha sık görülür. 2 basamakta, 0-67, 68-99'dan% 0.3 daha sık görülür. Eğer rasgele bir sayı dağılımına ihtiyacınız varsa, umarım bash kullanmıyorsunuzdur. Orijinal ile: ${RANDOM:0:1}size 1 veya 2 vermek için% 67.8 şansı var, ${RANDOM:0:2}sadece% 0.03 oranında size tek haneli bir numara verme şansı var (% 1 olmalı) ve her ikisinin de% 0.003 oranında 0 verme şansınız var. Bunun hala iyi olduğu durumlar vardır (örn. Tutarlı olmayan girdi).
jbo5112

12

0 ile 9 arasında rastgele sayı.

echo $((RANDOM%10))

2
Benim hatam, adam sayfasını düzgün okumadım. $RANDOM"0'dan 32767'ye kadar gider." Çoğunlukla 1 ile 3 arasında, birkaç kanatlı rastgele sayı ";)
David Newcomb

1
Ne? Yine de 0 ile 9 arasında olacaktır, ancak 8 ve 9'un başka bir cevapta belirtildiği gibi 0 ila 7'den biraz daha düşük olma olasılığı olacaktır.
kini

6

Bir linux sistemi kullanıyorsanız / dev / random'den rastgele bir sayı veya / dev / urandom'dan . Yeterli rasgele sayılar yoksa dikkatli olun / dev / random bloke olur. Rasgelelik üzerinde hıza ihtiyacınız varsa / dev / urandom kullanın.

Bu "dosyalar", işletim sistemi tarafından oluşturulan rasgele sayılarla doldurulur. Doğru veya sahte rasgele sayılar alırsanız, sisteminize / dev / random uygulamasına bağlıdır. Fare, sabit sürücü, ağ gibi aygıt sürücülerinden toplanan gürültü ile gerçek rasgele sayılar oluşturulur.

Sen ile dosyadan rasgele sayılar alabilirsiniz gg


5

Bu fikirlerden birkaçını aldım ve çok sayıda rastgele sayı gerekiyorsa hızlı bir şekilde çalışması gereken bir işlev yaptım.

odÇok sayıda rastgele sayıya ihtiyacınız varsa arama yapmak pahalıdır. Bunun yerine bir kez ararım ve / dev / urandom'dan 1024 rasgele sayı kaydederim. Ne zamanrand , son rasgele sayı döndürülür ve ölçeklendirilir. Daha sonra önbellekten kaldırılır. Önbellek boş olduğunda, başka bir 1024 rasgele sayı okunur.

Misal:

rand 10; echo $RET

0 ile 9 (dahil) arasında RET cinsinden rastgele bir sayı döndürür.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

GÜNCELLEME: küçük N. 32 rasgele sayı bit (bu durumda) belirten birlikte kullanılırsa bu aynı zamanda rastgele bit boşa tüm N. için çok iyi çalışmaz 0 ve 9 (10 * arasında 9 rastgele sayılar için yeterli entropiye sahiptir 9 = 1,000,000,000 <= 2 * 32) her 32 rastgele kaynak değerinden birden fazla rasgele sayı çıkarabiliriz.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done

Bunu denedim -% 100 cpu 10 saniye sürdü ve sonra TÜMÜ'nde rastgele görünmeyen 10 sayı yazdırdı.
Carlo Wood

Şimdi hatırlıyorum. Bu kod 100.000 rasgele sayı üretir. Ne kadar rasgele olduğuna bakmak için her birini bir 'çöp kutusuna' koyar. 10 kutu var. 0 ile 9 arasındaki her rasgele sayı eşit derecede olasıysa bu sayılar benzer olmalıdır. Her sayıyı yazdırmak istiyorsanız, randBetween 10'dan sonra $ RET yankılayın
philcolbourn

od -An -tu4 -N40 /dev/urandomboşlukla ayrılmış 10 rastgele imzasız 32 bit tam sayı üretecektir. bir dizide saklayabilir ve daha sonra kullanabilirsiniz. kodunuz aşırıya kaçmış gibi görünüyor.
Ali

@Ali, OP 32 bit veya başka bir boyutta rastgele sayı istediklerini belirtmedi. Ben ve bazı diğerleri bu soruyu bir aralık içinde rastgele bir sayı sağlamak olarak yorumladık. Rand fonksiyonum bu hedefe ulaşıyor ve tükendiğinde programların bloke olmasına neden olan entropi kaybını da azaltıyor. od on / dev / urandom yalnızca 2 ^ N bit rasgele sayılar döndürür ve OP'nin bir dizide birden çok değeri depolaması, bu diziden sırayla çıkarması ve bu diziyi yeniden doldurması gerekir. Belki bunu bir cevap olarak kodlayabilir ve diğer rasgele sayı aralıklarını işleyebilirsiniz?
philcolbourn

@philcolbourn, OP'nin ne tür bir rasgele sayı istediğini belirtmemesi konusunda haklısınız ve dikkatimi kaçırdı. Ama o sadece sordu: "oluşturmak için nasıl bir bash rasgele sayı?". Demek istediğim, o sadece bir rastgele sayı istedi. Gerçi bu eleştirmen önceki yorumum için de geçerlidir ( 10 rastgele sayı üreterek ).
Ali

5

/ Dev / random veya / dev / urandom karakter özel dosyalarından okumak yoludur.

Bu cihazlar okunduğunda gerçekten rasgele sayılar döndürür ve uygulama yazılımının şifreleme için güvenli anahtarlar seçmesine yardımcı olmak üzere tasarlanmıştır. Bu rastgele sayılar, çeşitli rasgele olayların katkıda bulunduğu bir entropi havuzundan çıkarılır. {LDD3, Jonathan Corbet, Alessandro Rubini ve Greg Kroah-Hartman]

Bu iki dosya özellikle çekirdek rasgeleleştirmesi için arabirimdir

void get_random_bytes_arch(void* buf, int nbytes)

bu işlev, donanımın uygulandığı (genellikle) olan donanımdan gerçekten rasgele bayt çeker veya entropi havuzundan (fare ve klavye kesintileri gibi olaylar ve SA_SAMPLE_RANDOM'a kayıtlı diğer kesintiler arasındaki zamanlamalardan oluşur).

dd if=/dev/urandom count=4 bs=1 | od -t d

Bu çalışır, ancak ddstdout için gereksiz çıktı yazar . Aşağıdaki komut sadece ihtiyacım olan tamsayıyı veriyor. Hatta aritmetik genişleme verilen bitmask ayarlayarak ihtiyacım olduğu gibi rastgele bit belirtilen sayıda alabilirsiniz:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))

3

Ne dersin:

perl -e 'print int rand 10, "\n"; '

1
Kriptografik olarak güvenli bir rasgele sayı için / dev / urandom'dan okumanız veya Crypt :: Random kütüphanelerini kullanmanız gerekir.
kh

3

Belki biraz geç kaldım, ama jotBash aralığında bir rastgele sayı oluşturmak için kullanmaya ne dersiniz ?

jot -r -p 3 1 0 1

Bu -r, 3 ondalık basamak hassasiyetine ( -p) sahip rastgele ( ) bir sayı oluşturur . Bu durumda, 0 ile 1 ( 1 0 1) arasında bir sayı alırsınız . Sıralı verileri de yazdırabilirsiniz. El kitabına göre rastgele sayının kaynağı:

Rastgele sayılar hiçbir tohum belirtilmediğinde arc4random (3) ve bir tohum verildiğinde rastgele (3) ile elde edilir.


1
Yüklenmesi gerekir: sudo apt install athena-jot
xerostomus

3

@Nelson, @Barun ve @Robert'in harika cevaplarına dayanarak, rastgele sayılar üreten bir Bash betiği.

  • Kaç basamak istediğinizi üretebilirsiniz.
  • her rakam ayrı ayrı üretilir ve /dev/urandombu da Bash'in yerleşik kodundan çok daha iyidir$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num

Tam olarak ne olduğumu.
Prometheus

2

0 ile n (işaretli 16 bit tam sayı) aralığında rasgele sayı oluşturun. Sonuç $ RAND değişkeninde ayarlandı. Örneğin:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done

1

Bir programın rastgele dallanması veya evet / hayır; 1/0; doğru / yanlış çıktı:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

16383'ü hatırlamak için tembelseniz:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
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.