Come generare un numero casuale in Bash?


236

Come generare un numero casuale all'interno di un intervallo in Bash?


21
Quanto deve essere casuale?
bdonlan,

Risposte:


283

Usa $RANDOM. È spesso utile in combinazione con l'aritmetica semplice della shell. Ad esempio, per generare un numero casuale compreso tra 1 e 10 (incluso):

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

Il generatore attuale è nella variables.cfunzione brand(). Le versioni precedenti erano un semplice generatore lineare. La versione 4.0 bashutilizza un generatore che cita un documento del 1985, il che presumibilmente significa che è una discreta fonte di numeri pseudocasuali. Non lo userei per una simulazione (e certamente non per la crittografia), ma è probabilmente adeguato per le attività di scripting di base.

Se stai facendo qualcosa che richiede numeri casuali seri che puoi usare /dev/randomo /dev/urandomse sono disponibili:

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

21
Stai attento qui. Mentre questo va bene in un pizzico, fare l'aritmetica su numeri casuali può influenzare notevolmente la casualità del tuo risultato. nel caso di $RANDOM % 10, 8 e 9 sono misurabili (anche se marginalmente) meno probabili di 0-7, anche se $RANDOMè una solida fonte di dati casuali.
dimo414,

3
@ dimo414 Sono curioso di "marginalmente", hai una fonte dove posso saperne di più su questo?
PascalVKooten,

58
Modulando il tuo input casuale, stai "forando il piccione " i risultati. Poiché $RANDOMgamma 's è 0-32767il numero 0- 7mappare 3277diversi ingressi possibili, ma 8e 9può essere prodotto solo 3276modi diversi (perché 32768e 32769non sono possibili). Questo è un problema minore per gli hack rapidi, ma significa che il risultato non è uniformemente casuale. Le librerie casuali, come quelle di Java Random, offrono funzioni per restituire correttamente un numero casuale uniforme in un determinato intervallo, anziché semplicemente modificare un numero non divisibile.
dimo414,

1
@JFSebastian è molto vero: il problema con modulo è che può rompere l'uniformità di qualsiasi RNG, non solo cattivi PRNG, ma grazie per averlo chiamato.
dimo414,

14
Solo per il contesto, il pigeonholing di base per% 10 significa che 8 e 9 hanno circa il 0,03% in meno di probabilità che si verifichino 0-7. Se lo script della tua shell richiede numeri casuali uniformi più accurati di così, usa sicuramente un meccanismo più complesso e appropriato.
Nelson,

71

Si prega di vedere $RANDOM:

$RANDOM è una funzione Bash interna (non una costante) che restituisce un numero intero pseudocasuale nell'intervallo 0 - 32767. Non deve essere utilizzato per generare una chiave di crittografia.


1
Non 32767ha alcun significato particolare?
Jin Kwon il

14
@JinKwon 32767è 2^16 / 2 - 1il limite superiore per un numero intero a 16 bit con segno.
Jeffrey Martinez,

@JinKwon potresti chiarire perché non dici che lo è 2^15 - 1? È equivalente, quindi sono solo curioso di sapere se c'è qualche contesto che mi manca?
Brett Holman,

11
@BrettHolman Penso che stesse cercando di sottolineare la parte "firmata" dell'intero a 16 bit con segno. 2 ^ 16 valori, divisi a metà per ints postivi e negativi.
codice

Questa risposta non risponde alla domanda.
marmocchio

48

Puoi anche usare shuf (disponibile in coreutils).

shuf -i 1-100000 -n 1

Come si passa in vari come fine gamma? Ho ottenuto questo:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
Aggiungi un punto $vardi fine gamma, in questo modo:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Preferisco questa opzione in quanto è facile generare N numeri casuali con -n. Ad esempio, genera 5 numeri casuali tra 1 e 100 :shuf -i 1-100 -n 5
aerijman

Per quanto ho capito, i numeri non sono casuali. Se specifichi shuf -i 1-10 -n 10otterrai tutti i numeri da 1 a 10 esattamente uno. Se specifichi -n 15otterrai comunque solo quei 10 numeri esattamente una volta. Questo è davvero solo mescolare, non generare numeri casuali.
Radlan,

Per ottenere numeri casuali con sostituzione: -r
Geoffrey Anderson

36

Prova questo dalla tua shell:

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

Qui, -t dspecifica che il formato di output deve essere decimale; -N 1dice di leggere un byte da /dev/urandom.


2
La domanda richiede numeri in un intervallo.
JSycamore,

9
puoi rimuovere gli spazi:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert,

23

puoi anche ottenere un numero casuale da awk

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

1
+1 sai, all'inizio ho pensato perché avresti mai voluto farlo in questo modo, ma in realtà mi piace molto.
zelanix,

1
Grazie per aver dato una soluzione che include la semina. Non sono riuscito a trovarlo da nessuna parte!
so.very.tired

2
+1 per la semina. Vale la pena ricordare che srand()il seme è il tempo corrente della CPU. Se è necessario specificare un seme specifico, in modo da poter duplicare RNG, utilizzare srand(x)dove si xtrova il seme. Inoltre, citato dal manuale di funzioni numeriche di GNU awk, "diverse implementazioni di awk usano internamente diversi generatori di numeri casuali". Il risultato è che se sei interessato a generare una distribuzione statistica, dovresti aspettarti lievi variazioni che vanno da un runtime al successivo su piattaforme diverse (tutte in esecuzione awko gawk).
Cbhihe,

18

C'è $ RANDOM. Non so esattamente come funzioni. Ma funziona Per i test, puoi fare:

echo $RANDOM

16

Mi piace questo trucco:

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

...


7
$ RANDOM è compreso tra 0 e 32767. Si finirà con un numero maggiore di numeri che iniziano con 1, 2 o 3, rispetto a 4-9. Se stai bene con una distribuzione sbilanciata, funzionerà bene.
jbo5112,

2
@ jbo5112 hai perfettamente ragione, che ne dici di visualizzare l'ultima cifra? echo $ {RANDOM: 0-1} per una cifra, $ {RANDOM: 0-2} per due cifre ...?
Fraff

4
Se usi le ultime cifre, sarà piuttosto buono, ma includerà 0 e 00. Su una singola cifra, 0-7 si verificherà con lo 0,03% più spesso dell'8-9. Su 2 cifre, 0-67 avverrà lo 0,3% più spesso rispetto a 68-99. Se hai bisogno di una distribuzione casuale dei numeri così buona, spero non stai usando bash. Con l'originale: ${RANDOM:0:1}ha una probabilità del 67,8% di darti un 1 o un 2, ${RANDOM:0:2}ha solo una probabilità dello 0,03% di darti un numero a una cifra (dovrebbe essere dell'1%) ed entrambi hanno una probabilità dello 0,003% di darti uno 0 Vi sono ancora casi d'uso in cui ciò va bene (ad esempio input non coerente).
jbo5112,

12

Numero casuale compreso tra 0 e 9 inclusi.

echo $((RANDOM%10))

2
Mio male, non ho letto correttamente la pagina man. $RANDOMva solo da 0 a 32767. Avrebbe dovuto dire "Numero casuale principalmente tra 1 e 3, con poche ali";)
David Newcomb

1
Che cosa? Sarà comunque compreso tra 0 e 9, sebbene 8 e 9 abbiano probabilità leggermente inferiori rispetto a 0 tra 7, come indicato in un'altra risposta.
Kini,

6

Se stai usando un sistema Linux puoi ottenere un numero casuale da / dev / random o / dev / urandom. Fai attenzione / dev / random bloccherà se non ci sono abbastanza numeri casuali disponibili. Se hai bisogno di velocità sulla casualità usa / dev / urandom.

Questi "file" saranno riempiti con numeri casuali generati dal sistema operativo. Dipende dall'implementazione di / dev / random sul tuo sistema se ottieni numeri veri o pseudo casuali. I veri numeri casuali vengono generati con l'aiuto del rumore raccolto dai driver di dispositivo come mouse, disco rigido, rete.

È possibile ottenere numeri casuali dal file con dd


5

Ho preso alcune di queste idee e creato una funzione che dovrebbe svolgere rapidamente se sono richiesti molti numeri casuali.

chiamare odè costoso se hai bisogno di molti numeri casuali. Invece lo chiamo una volta e memorizzo 1024 numeri casuali da / dev / urandom. quandorand viene chiamato, l'ultimo numero casuale viene restituito e ridimensionato. Viene quindi rimosso dalla cache. Quando la cache è vuota, vengono letti altri 1024 numeri casuali.

Esempio:

rand 10; echo $RET

Restituisce un numero casuale in RET tra 0 e 9 inclusi.

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

AGGIORNAMENTO: Questo non funziona così bene per tutti N. Inoltre spreca bit casuali se usato con N. piccolo Notando che (in questo caso) un numero casuale a 32 bit ha abbastanza entropia per 9 numeri casuali tra 0 e 9 (10 * 9 = 1.000.000.000 <= 2 * 32) possiamo estrarre più numeri casuali da ciascun valore di 32 fonti casuali.

#!/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

Ho provato questo: ci sono voluti 10 secondi del 100% della CPU e poi ho stampato 10 numeri che non sembravano tutti casuali.
Carlo Wood,

Adesso mi ricordo. Questo codice genera 100.000 numeri casuali. Mette ciascuno in un 'cestino' per vedere quanto è casuale. Ci sono 10 scomparti. Questi numeri dovrebbero essere simili se ogni numero casuale compreso tra 0 e 9 è ugualmente probabile. Se vuoi stampare ogni numero, fai eco $ RET dopo randB fra 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomgenererà 10 numeri interi a 32 bit senza segno casuali separati da spazi bianchi. puoi memorizzarlo in un array e usarlo in seguito. il tuo codice sembra essere eccessivo.
Ali,

@Ali, OP non ha specificato che desideravano 32 bit né qualsiasi altro numero casuale di dimensioni. Io e alcuni altri abbiamo interpretato questa domanda come fornire un numero casuale all'interno di un intervallo. La mia funzione rand raggiunge questo obiettivo e riduce anche la perdita di entropia che, se esaurita, provoca il blocco dei programmi. od on / dev / urandom restituisce solo numeri casuali a 2 ^ N bit e OP dovrebbe quindi memorizzare più valori in un array, estraendoli in sequenza da questo array e riempiendo questo array. Forse puoi codificare questo come una risposta e gestire altri intervalli di numeri casuali?
philcolbourn,

@philcolbourn, hai ragione sul fatto che l'OP non specifica quale tipo di numero casuale vuole e ha perso la mia attenzione. Ma ha solo chiesto: "Come generare un numero casuale in bash?". Il mio punto è che ha chiesto solo un numero casuale. Anche se questo critico si applica anche al mio commento precedente (che genera 10 numeri casuali).
Ali,

5

La lettura da file speciali / dev / random o / dev / urandom è la strada da percorrere.

Questi dispositivi restituiscono numeri veramente casuali quando vengono letti e sono progettati per aiutare il software applicativo a scegliere chiavi sicure per la crittografia. Tali numeri casuali sono estratti da un pool di entropia che è contribuito da vari eventi casuali. {LDD3, Jonathan Corbet, Alessandro Rubini e Greg Kroah-Hartman]

Questi due file sono l'interfaccia per la randomizzazione del kernel, in particolare

void get_random_bytes_arch(void* buf, int nbytes)

che disegna byte veramente casuali dall'hardware se tale funzione è implementata dall'hardware (di solito lo è) o attinge dal pool di entropia (composto da temporizzazioni tra eventi come interruzioni del mouse e della tastiera e altri interruzioni registrate con SA_SAMPLE_RANDOM).

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

Funziona, ma scrive l'output non necessario da ddstdout. Il comando seguente fornisce solo l'intero di cui ho bisogno. Posso persino ottenere il numero specificato di bit casuali di cui ho bisogno regolando la maschera di bit data all'espansione aritmetica:

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

3

Che dire:

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

1
Per un numero casuale crittograficamente sicuro, devi leggere da / dev / urandom o utilizzare le librerie Crypt :: Random.
Kh

3

Forse sono un po 'troppo tardi, ma che ne dici di usare jotper generare un numero casuale all'interno di un intervallo in Bash?

jot -r -p 3 1 0 1

Questo genera un -rnumero casuale ( ) con 3 decimali di precisione ( -p). In questo caso particolare, otterrai un numero compreso tra 0 e 1 ( 1 0 1). È inoltre possibile stampare dati sequenziali. La fonte del numero casuale, secondo il manuale, è:

I numeri casuali sono ottenuti attraverso arc4random (3) quando non viene specificato alcun seme e attraverso random (3) quando viene dato un seme.


1
Deve essere installato: sudo apt install athena-jot
xerostomus

3

Basato sulle grandi risposte di @Nelson, @Barun e @Robert, ecco uno script Bash che genera numeri casuali.

  • Può generare quante cifre vuoi.
  • ogni cifra è generata separatamente, il /dev/urandomche è molto meglio di quello incorporato di Bash$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

Esattamente quello che stavo cercando.
Prometeo

2

Genera un numero casuale nell'intervallo da 0 a n (numero intero a 16 bit con segno). Risultato impostato nella variabile $ RAND. Per esempio:

#!/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

Diramazione casuale di un programma o sì / no; 1/0; output vero / falso:

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

di se sei pigro da ricordare 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.