web-dev-qa-db-fra.com

Générer des nombres aléatoires dans une plage spécifique

Après avoir googlé un peu, je n'ai pas pu trouver un moyen simple d'utiliser une commande Shell pour générer un nombre entier décimal aléatoire inclus dans une plage spécifique, c'est-à-dire entre un minimum et un maximum.

Je lis à propos de /dev/random, /dev/urandom et $RANDOM, mais rien de tout cela ne peut faire ce dont j'ai besoin.

Existe-t-il une autre commande utile ou un moyen d'utiliser les données précédentes?

94
BowPark

Dans le POSIX toolchest, vous pouvez utiliser awk:

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

Utilisez pas comme source pour générer des mots de passe ou des données secrètes par exemple, comme avec la plupart des implémentations awk, le nombre peut facilement être deviné en fonction de l'heure à laquelle la commande a été exécutée.

Avec de nombreuses implémentations awk, cette commande exécutée deux fois dans la même seconde vous donnera généralement la même sortie.

49
Stéphane Chazelas

Vous pouvez essayer shuf depuis GNU coreutils:

shuf -i 1-100 -n 1
151
cuonglm

iota

Sur BSD et OSX, vous pouvez utiliser jot pour renvoyer un seul aléatoire (-r) nombre de l'intervalle min à max, inclus.

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

Problème de distribution

Malheureusement, la plage et la distribution des nombres générés de manière aléatoire sont influencées par le fait que jot utilise l'arithmétique à virgule flottante double précision en interne et printf (3) pour le format de sortie, ce qui provoque des problèmes d'arrondi et de troncature. Par conséquent, min et max de l'intervalle sont générés moins fréquemment, comme illustré:

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

Sur OS X 10.11 (El Capitan), cela semble avoir été corrigé:

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

et...

$ 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

Résoudre le problème de distribution

Pour les anciennes versions d'OS X, heureusement, il existe plusieurs solutions de contournement. La première consiste à utiliser la conversion entière printf (3). La seule mise en garde est que l'intervalle maximum devient maintenant max+1. En utilisant le formatage entier, nous obtenons une distribution équitable sur tout l'intervalle:

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

La solution parfaite

Enfin, pour obtenir un jet équitable des dés en utilisant la solution de contournement, nous avons:

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

Devoirs supplémentaires

Voir jot (1) pour les détails sanglants de mathématiques et de mise en forme et bien d'autres exemples.

33
Clint Pachl

Le $RANDOM la variable n'est normalement pas un bon moyen de générer de bonnes valeurs aléatoires. La sortie de /dev/[u]random doit également être converti en premier.

Un moyen plus simple consiste à utiliser des langues de niveau supérieur, comme par exemple python:

Pour générer une variable entière aléatoire entre 5 et 10 (5 <= N <= 10), utilisez

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

Ne l'utilisez pas pour des applications cryptographiques.

16
jofel

Vous pouvez obtenir le nombre aléatoire via urandom

head -200 /dev/urandom | cksum

Production:

3310670062 52870

Pour récupérer la partie du numéro ci-dessus.

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

Ensuite, la sortie est

3310670062

14
zangw

Pour générer une variable entière aléatoire entre 5 et 10 (y compris les deux), utilisez

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

% fonctionne comme un opérateur modulo.

Il existe probablement de meilleures façons de convertir la variable aléatoire $RANDOM à une plage spécifique. Ne l'utilisez pas pour des applications cryptographiques ou dans le cas où vous avez besoin de vraies variables aléatoires à répartition égale (comme pour les simulations).

10
jofel

Peut-être que le UUID (sous Linux) peut être utilisé pour récupérer le nombre aléatoire

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

Pour obtenir le nombre aléatoire entre 70 et 100

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

# echo $(( $RANDOM % 256 )) produira un nombre "aléatoire" entre 0 et 255 dans les dialectes modernes * sh.

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

Cela générera un nombre hexadécimal de 8 chiffres.

4
Tom Cooper

Pour rester entièrement dans bash et utiliser la variable $ RANDOM mais éviter la distribution inégale:

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

Cet exemple fournirait des nombres aléatoires de 20 à 29.

2
Than Angell

La distribution est bonne:

pour ((i = 1; i <= 100000; i ++)) faire écho $ ((RANDOM% (20 - 10 + 1) + 10)); fait | sort -n | uniq -c

compter la valeur

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

1
SteveK

en utilisant RANDOM

MAX=999999                      #Upper Range
MIN=100000                      #Lower Range
DIFF=$((MAX-MIN+1))             #+1 to inlcude upper limit
R=$(($(($RANDOM%$DIFF))+MIN))

en utilisant SHUF

MIN=1000
MAX=9999
COUNT=1                         #count of Random Numbers to be generated
shuf -i $MIN-$MAX -n $COUNT
shuf -i 1000-9999 -n 1

en utilisant/dev/random

od -An -N2 -i /dev/random       #Generates a 2 byte Number, -N2=Generates 2 Random Number btween range 0-65535
0
Aashutosh