Genera numeri casuali in un intervallo specifico


84

Dopo aver cercato su Google un po 'non sono riuscito a trovare un modo semplice per utilizzare un comando shell per generare un numero intero decimale casuale incluso in un intervallo specifico, compreso tra un minimo e un massimo.

Ho letto /dev/random, /dev/urandome $RANDOM, ma nessuno di questi posso fare quello che mi serve.

Esiste un altro comando utile o un modo per utilizzare i dati precedenti?




Risposte:


46

Nel toolchest POSIX è possibile utilizzare awk:

awk -v min=5 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

Da non utilizzare che come fonte per generare password o dati segreti, per esempio, come la maggior parte awkdelle implementazioni, il numero può essere facilmente indovinata in base al tempo che il comando è stato eseguito.

Con molte awkimplementazioni, quel comando eseguito due volte nello stesso secondo ti darà generalmente lo stesso output.


1
+1 .. Come utilizzato in uno script per assegnare l'output a una variabile (senza introdurre un carattere di nuova riga e utilizzare il riempimento zero per due posizioni):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher

Si basa sul tempo? da quando ho ottenuto lo stesso valore in 2 corse consecutive ...
Shai Alon

@ShaiAlon l'ora corrente viene spesso utilizzata come valore seed predefinito per srand () quindi di solito sì, si basa sul tempo, vedere la frase di Stéphane a riguardo nella sua risposta. Puoi aggirarlo cambiando il seme iniziale in un numero casuale con awk -v min=5 -v max=10 -v seed="$(od -An -N4 -tu4 /dev/urandom)" 'BEGIN{srand(seed+0); print int(min+rand()*(max-min+1))}'. È possibile utilizzare $RANDOMinvece di, od ... /dev/randomma il valore del seme è compreso nell'intervallo relativamente piccolo da 0 a 32767 e quindi si otterranno probabilmente notevoli ripetizioni nell'output nel tempo.
Ed Morton,

142

Puoi provare shufdai coreutils GNU:

shuf -i 1-100 -n 1

Questo funziona anche con Busybox shuf.
cov

Funziona anche in raspbian e GitBash.
nPcomp

30

annotare in fretta

Su BSD e OSX puoi usare jot per restituire un singolo -rnumero casuale ( ) dall'intervallo mina max, compreso.

$ min=5
$ max=10
$ jot -r 1 $min $max

Problema di distribuzione

Sfortunatamente, l'intervallo e la distribuzione di numeri generati casualmente è influenzato dal fatto che jot utilizza internamente l'aritmetica in virgola mobile a doppia precisione e printf (3) per il formato di output, che causa problemi di arrotondamento e troncamento. Pertanto, gli intervalli mine maxvengono generati meno frequentemente, come dimostrato:

$ jot -r 100000 5 10 | sort -n | uniq -c
9918  5
20176 6
20006 7
20083 8
19879 9
9938  10

Su OS X 10.11 (El Capitan) questo sembra essere stato corretto:

$ jot -r 100000 5 10 | sort -n | uniq -c
16692 5
16550 6
16856 7
16579 8
16714 9
16609 10  

e...

$ jot -r 1000000 1 10 | sort -n | uniq -c
100430 1
99965 2
99982 3
99796 4
100444 5
99853 6
99835 7
100397 8
99588 9
99710 10

Risolvere il problema di distribuzione

Per le versioni precedenti di OS X, fortunatamente ci sono diverse soluzioni alternative. Uno è usare la conversione intera printf (3). L'unica avvertenza è che l'intervallo massimo ora diventa max+1. Usando la formattazione dei numeri interi, otteniamo una distribuzione equa nell'intero intervallo:

$ jot -w %i -r 100000 5 11 | sort -n | uniq -c
16756 5
16571 6
16744 7
16605 8
16683 9
16641 10

La soluzione perfetta

Infine, per ottenere un tiro corretto dei dadi usando la soluzione alternativa, abbiamo:

$ min=5
$ max_plus1=11  # 10 + 1
$ jot -w %i -r 1 $min $max_plus1

Compiti extra

Vedi jot (1) per i dettagli di formattazione e matematica gory e molti altri esempi.


15

La $RANDOMvariabile non è normalmente un buon modo per generare buoni valori casuali. Anche l'output di /dev/[u]randomnecessità deve essere convertito per primo.

Un modo più semplice è usare linguaggi di livello superiore, come ad esempio python:

Per generare una variabile intera casuale tra 5 e 10 (5 <= N <= 10), utilizzare

python -c "import random; print random.randint(5,10)"

Non utilizzarlo per applicazioni crittografiche.


Questo è stato utile Grazie mille :) Dato che firmato spesso ha funzionato con Unix-> Solaris 10, i programmi di utilità GNU non sono integrati di default. Questo ha funzionato comunque.
propatienza,

11

Puoi ottenere il numero casuale attraverso urandom

head -200 /dev/urandom | cksum

Produzione:

3310670062 52870

Per recuperare una parte del numero sopra.

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

Quindi l'output è

3310670062


head -200(o il suo equivalente POSIX head -n 100) restituisce le prime 200 righe di /dev/urandom. /dev/urandomnon è un file di testo, tale comando potrebbe restituire da 200 byte (tutti 0x0a, LF in ASCII) all'infinito (se il byte 0xa non viene mai restituito) ma i valori tra 45k e 55k sono i più probabili. cksum restituisce un numero a 32 bit, quindi è inutile ottenere più di 4 byte da /dev/urandom.
Stéphane Chazelas,

7

Per generare una variabile intera casuale tra 5 e 10 (inclusi entrambi), utilizzare

echo $(( RANDOM % (10 - 5 + 1 ) + 5 ))

% funziona come un operatore modulo.

Probabilmente ci sono modi migliori per convertire la variabile casuale $RANDOMin un intervallo specifico. Non utilizzarlo per applicazioni crittografiche o nei casi in cui siano necessarie variabili casuali reali, equamente distribuite (come per le simulazioni).


3
In molte implementazioni della shell che hanno $ RANDOM (specialmente quelle più vecchie, specialmente bash), questo non è molto casuale. Nella maggior parte delle shell, il numero è compreso tra 0 e 65535, quindi qualsiasi intervallo la cui larghezza non è una potenza di due avrà discrepanza nella distribuzione della probabilità. (in questo caso, i numeri da 5 a 8 avranno una probabilità di 10923/65536, mentre 9 e 10 avranno una probabilità di 10922/65536). Più ampia è la gamma, maggiore è la discrepanza.
Stéphane Chazelas,

Siamo spiacenti, 32767 non 65535, quindi il calcolo sopra è errato (ed è effettivamente peggio).
Stéphane Chazelas,

@ StéphaneChazelas Ho avuto questo in mente mentre scrivevo che ci sono modi migliori ...
jofel


4

Forse UUID(su Linux) può essere usato per recuperare il numero casuale

$ cat /proc/sys/kernel/random/uuid
cdd52826-327d-4355-9737-895f58ad11b4

Per ottenere il numero casuale tra 70e100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid

3
cat /dev/urandom | tr -dc 'a-fA-F0-9' | fold -w 8 | head -n 1

Ciò genererà un numero esadecimale lungo 8 cifre.


1

Per rimanere completamente all'interno di bash e utilizzare la variabile $ RANDOM ma evitare la distribuzione irregolare:

#!/bin/bash
range=10 
floor=20

if [ $range -gt 32768 ]; then
echo 'range outside of what $RANDOM can provide.' >&2
exit 1 # exit before entering infinite loop
fi 

max_RANDOM=$(( 2**15/$range*$range ))
r=$RANDOM
until [ $r -lt $max_RANDOM ]; do
r=$RANDOM
done
echo $(( r % $range + $floor ))

Questo esempio fornirebbe numeri casuali da 20 a 29.


$rdeve essere inizializzato su 65535 o altro valore elevato per evitare un [: -lt: unary operator expectederrore.
LawrenceC,

Correzione dell'inizializzazione $ r prima del test del ciclo. Grazie @LawrenceC!
Angell l'

0

La distribuzione è buona:

per ((i = 1; i <= 100000; i ++)) echo $ ((RANDOM% (20-10 + 1) + 10)); fatto | ordina -n | uniq -c

contare il valore

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

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.