Vous êtes sur la page 1sur 12

16/11/2021 00:33 9.6.

 $RANDOM : générer un nombre aléatoire

9.6. $RANDOM : générer un nombre aléatoire

$RANDOM est une fonction interne Bash (pas une constante)


renvoyant un entier pseudo-aléatoire
[39]
dans l'intervalle 0 - 32767. Il ne devrait
pas être utilisé pour générer
une clé de chiffrement.

Exemple 9.26. Générer des nombres aléatoires

#!/bin/bash

# $RANDOM renvoie un entier différent à chaque appel.

# Échelle : 0 - 32767 (entier signé sur 16 bits).

NBMAX=10

index=1

echo

echo "$NBMAX nombres aléatoires :"

echo "-----------------"

while [ "$index" -le $NBMAX ] # Génère 10 ($NBMAX) entiers aléatoires.

do

nombre=$RANDOM

echo $nombre

let "index += 1" # Incrémente l'index.

done

echo "-----------------"

# Si vous avez besoin d'un entier aléatoire dans une certaine échelle, utilisez

#+ l'opérateur 'modulo'.

# Il renvoie le reste d'une division.

ECHELLE=500

echo

nombre=$RANDOM

let "nombre %= $ECHELLE"

# ^^

echo "Nombre aléatoire inférieur à $ECHELLE --- $nombre"

echo

# Si vous avez besoin d'un entier aléatoire supérieur à une borne, alors

#+ faites un test pour annuler tous les nombres en dessous de cette borne.

PLANCHER=200

nombre=0 #initialise

while [ "$nombre" -le $PLANCHER ]

do

nombre=$RANDOM

done

echo "Nombre aléatoire supérieur à $PLANCHER --- $nombre"

echo

# Examinons une alternative simple à la boucle ci-dessus

# let "nombre = $RANDOM + $PLANCHER"

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 1/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

# Ceci éliminerait la boucle while et s'exécuterait plus rapidement.

# Mais, il resterait un problème. Lequel ?

# Combine les deux techniques pour récupérer un nombre aléatoire

# compris entre deux limites.

nombre=0 #initialise

while [ "$nombre" -le $PLANCHER ]

do

nombre=$RANDOM

let "nombre %= $ECHELLE" # Ramène $nombre dans $ECHELLE.

done

echo "Nombre aléatoire compris entre $PLANCHER et $ECHELLE --- $nombre"

echo

# Génère un choix binaire, c'est-à-dire "vrai" ou "faux".

BINAIRE=2

T=1

nombre=$RANDOM

let "nombre %= $BINAIRE"

# Notez que let "nombre >>= 14" donne une meilleure distribution aléatoire

# (les décalages droits enlèvent tout sauf le dernier nombre binaire).

if [ "$nombre" -eq $T ]
then

echo "VRAI"

else

echo "FAUX"

fi

echo

# Peut générer un lancer de dés

SPOTS=6 # Modulo 6 donne une échelle de 0 à 5.

# Incrémenter de 1 donne l'échelle désirée, de 1 à 6.

# Merci, Paulo Marcel Coelho Aragao, pour cette simplification.

die1=0

die2=0

# Serait-il mieux de seulement initialiser SPOTS=7 et de ne pas ajouter 1 ?

# Pourquoi ou pourquoi pas ?

# Jette chaque dé séparément, et donne ainsi une chance correcte.

let "die1 = $RANDOM % $SPOTS +1" # Le premier.

let "die2 = $RANDOM % $SPOTS +1" # Et le second.

# Quelle opération arithmétique ci-dessus a la plus grande précédence

# le modulo (%) ou l'addition (+) ?

let "throw = $die1 + $die2"

echo "Throw of the dice = $throw"

echo

exit 0

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 2/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

Exemple 9.27. Piocher une carte au hasard dans un


tas

#!/bin/bash

# pick-card.sh

# Ceci est un exemple pour choisir au hasard des éléments d'un tableau.

# Prenez une carte, n'importe quelle carte.

Suites="Carreau

Pique

Coeur

Trefle"

Denominations="2

10

Valet

Dame

Roi

As"

# Notez que le contenu de la variable continue sur plusieurs lignes.

suite=($Suites) # Lire dans une variable de type tableau.

denomination=($Denominations)

num_suites=${#suite[*]} # Compter le nombre d'éléments.

num_denominations=${#denomination[*]}

echo -n "${denomination[$((RANDOM%num_denominations))]} of "

echo ${suite[$((RANDOM%num_suites))]}

# $bozo sh pick-cards.sh

# Valet de trèfle

# Merci, "jipe", pour m'avoir indiqué cette utilisation de $RANDOM.

exit 0

Exemple 9.28. Simulation « Brownian Motion »

#!/bin/bash

# brownian.sh

# Author: Mendel Cooper


# Reldate: 10/26/07

# License: GPL3

# ----------------------------------------------------------------

# This script models Brownian motion:

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 3/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

#+ the random wanderings of tiny particles in a fluid,

#+ as they are buffeted by random currents and collisions.

#+ This is colloquially known as the "Drunkard's Walk."

# It can also be considered as a stripped-down simulation of a

#+ Galton Board, a slanted board with a pattern of pegs,

#+ down which rolls a succession of marbles, one at a time.

#+ At the bottom is a row of slots or catch basins in which

#+ the marbles come to rest at the end of their journey.

# Think of it as a kind of bare-bones Pachinko game.

# As you see by running the script,

#+ most of the marbles cluster around the center slot.

#+ This is consistent with the expected binomial distribution.

# As a Galton Board simulation, the script

#+ disregards such parameters as

#+ board tilt-angle, rolling friction of the marbles,

#+ angles of impact, and elasticity of the pegs.

# To what extent does this affect the accuracy of the simulation?

# ----------------------------------------------------------------

PASSES=500 # Number of particle interactions / marbles.

ROWS=10 # Number of "collisions" (or horiz. peg rows).

RANGE=3 # 0 - 2 output range from $RANDOM.

POS=0 # Left/right position.

RANDOM=$$ # Seeds the random number generator from PID

#+ of script.

declare -a Slots # Array holding cumulative results of passes.

NUMSLOTS=21 # Number of slots at bottom of board.

Initialize_Slots () { # Zero out all elements of the array.

for i in $( seq $NUMSLOTS )

do

Slots[$i]=0

done

echo # Blank line at beginning of run.

Show_Slots () {

echo -n " "

for i in $( seq $NUMSLOTS ) # Pretty-print array elements.

do

printf "%3d" ${Slots[$i]} # Allot three spaces per result.

done

echo # Row of slots:

echo " |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|"

echo " ^^"

echo # Note that if the count within any particular slot exceeds 99,

#+ it messes up the display.

# Running only(!) 500 passes usually avoids this.

Move () { # Move one unit right / left, or stay put.

Move=$RANDOM # How random is $RANDOM? Well, let's see ...

let "Move %= RANGE" # Normalize into range of 0 - 2.

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 4/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

case "$Move" in

0 ) ;; # Do nothing, i.e., stay in place.

1 ) ((POS--));; # Left.

2 ) ((POS++));; # Right.

* ) echo -n "Error ";; # Anomaly! (Should never occur.)

esac

Play () { # Single pass (inner loop).

i=0

while [ "$i" -lt "$ROWS" ] # One event per row.

do

Move

((i++));

done

SHIFT=11 # Why 11, and not 10?

let "POS += $SHIFT" # Shift "zero position" to center.

(( Slots[$POS]++ )) # DEBUG: echo $POS

Run () { # Outer loop.

p=0

while [ "$p" -lt "$PASSES" ]

do

Play

(( p++ ))

POS=0 # Reset to zero. Why?

done

# --------------

# main ()

Initialize_Slots

Run

Show_Slots

# --------------

exit $?

# Exercises:

# ---------

# 1) Show the results in a vertical bar graph, or as an alternative,

#+ a scattergram.

# 2) Alter the script to use /dev/urandom instead of $RANDOM.

# Will this make the results more random?

Jipe nous a indiqué un autre


ensemble de techniques pour générer des nombres aléatoires à
l'intérieur
d'un intervalle donné.

# Génére des nombres aléatoires entre 6 et 30.

rnumber=$((RANDOM%25+6))

# Générer des nombres aléatoires dans le même intervalle de 6 à 30,

#+ mais le nombre doit être divisible de façon exacte par 3.

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 5/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

rnumber=$(((RANDOM%30/3+1)*3))

# Notez que ceci ne fonctionnera pas tout le temps.

# Il échoue si $RANDOM%30 renvoie 0.

# Frank Wang suggère l'alternative suivante :

rnumber=$(( RANDOM%27/3*3+6 ))

Bill Gradwohl est parvenu à


une formule améliorée fonctionnant avec les numéros positifs.

rnumber=$(((RANDOM%(max-min+divisiblePar))/divisiblePar*divisiblePar+min))

Ici, Bill présente une fonction versatile renvoyant un numéro au


hasard entre deux valeurs spécifiques.

Exemple 9.29. Un nombre au hasard entre deux


valeurs

#!/bin/bash

# random-between.sh

# Nombre aléatoire entre deux valeurs spécifiées.

# Script par Bill Gradwohl, avec des modifications mineures par l'auteur du document.

# Utilisé avec les droits.

aleatoireEntre() {

# Génère un numéro aléatoire positif ou négatif

#+ entre $min et $max

#+ et divisible par $divisiblePar.

# Donne une distribution "raisonnablement aléatoire" des valeurs renvoyées.

# Bill Gradwohl - 1er octobre 2003

syntax() {

# Fonction imbriquée dans la fonction.

echo

echo "Syntax: aleatoireEntre [min] [max] [multiple]"

echo

echo "Attend au plus trois paramètres mais tous sont complètement optionnels."

echo "min est la valeur minimale"

echo "max est la valeur maximale"

echo "multiple spécifie que la réponse est un multiple de cette valeur."

echo " c'est-à-dire qu'une réponse doit être divisible de manière entière"

echo " par ce numéro."

echo

echo "Si cette valeur manque, l'aire par défaut supportée est : 0 32767 1"

echo "Un résultat avec succès renvoie 0. Sinon, la syntaxe de la fonction"

echo "est renvoyée avec un 1."

echo "La réponse est renvoyée dans la variable globale aleatoireEntreAnswer"

echo "Les valeurs négatives pour tout paramètre passé sont gérées correctement."

local min=${1:-0}

local max=${2:-32767}

local divisiblePar=${3:-1}

# Valeurs par défaut affectées, au cas où les paramètres ne sont pas passés à la

#+ fonction.

local x

local spread

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 6/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

# Assurez-vous que la valeur divisiblePar est positive.

[ ${divisiblePar} -lt 0 ] && divisiblePar=$((0-divisiblePar))

# Vérification.

if [ $# -gt 3 -o ${divisiblePar} -eq 0 -o ${min} -eq ${max} ]; then

syntax

return 1

fi

# Vérifiez si min et max ne sont pas inversés.

if [ ${min} -gt ${max} ]; then

# Les inversez.

x=${min}

min=${max}

max=${x}

fi

# Si min est lui-même non divisible par $divisiblePar,

#+ alors corrigez le min pour être à l'échelle.

if [ $((min/divisiblePar*divisiblePar)) -ne ${min} ]; then

if [ ${min} -lt 0 ]; then

min=$((min/divisiblePar*divisiblePar))

else

min=$((((min/divisiblePar)+1)*divisiblePar))

fi

fi

# Si max est lui-même non divisible par $divisiblePar,

#+ alors corrigez le max pour être à l'échelle.

if [ $((max/divisiblePar*divisiblePar)) -ne ${max} ]; then

if [ ${max} -lt 0 ]; then

max=$((((max/divisiblePar)-1)*divisiblePar))

else

max=$((max/divisiblePar*divisiblePar))

fi

fi

# ---------------------------------------------------------------------

# Maintenant, pour faire le vrai travail.

# Notez que pour obtenir une distribution correcte pour les points finaux,

#+ l'échelle des valeurs aléatoires doit être autorisée pour aller entre 0 et

#+ abs(max-min)+divisiblePar, et non pas seulement abs(max-min)+1.

# La légère augmentation produira une distribution correcte des points finaux.

# Changer la formule pour utiliser abs(max-min)+1 produira toujours des réponses

#+ correctes mais le côté aléatoire des réponses est erroné dans le fait que le

#+ nombre de fois où les points finaux ($min et $max) sont renvoyés est

#+ considérablement plus petit que lorsque la formule correcte est utilisée.

# ---------------------------------------------------------------------

spread=$((max-min))

# Omair Eshkenazi indique que ce test n'est pas nécessaire

#+ car max et min ont déjà été basculés.

[ ${spread} -lt 0 ] && spread=$((0-spread))

let spread+=divisiblePar

aleatoireEntreAnswer=$(((RANDOM%spread)/divisiblePar*divisiblePar+min))

return 0

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 7/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

# Néanmoins, Paulo Marcel Coelho Aragao indique que

#+ quand $max et $min ne sont pas divisibles par $divisiblePar,

#+ la formule échoue.

# Il suggère à la place la formule suivante :

# rnumber = $(((RANDOM%(max-min+1)+min)/divisiblePar*divisiblePar))

# Testons la fonction.

min=-14

max=20

divisiblePar=3

# Génère un tableau des réponses attendues et vérifie pour s'assurer que nous obtenons

#+ au moins une réponse si nous bouclons assez longtemps.

declare -a reponse

minimum=${min}

maximum=${max}

if [ $((minimum/divisiblePar*divisiblePar)) -ne ${minimum} ]; then

if [ ${minimum} -lt 0 ]; then

minimum=$((minimum/divisiblePar*divisiblePar))

else

minimum=$((((minimum/divisiblePar)+1)*divisiblePar))

fi

fi

# Si max est lui-même non divisible par $divisiblePar,

#+ alors corrigez le max pour être à l'échelle.

if [ $((maximum/divisiblePar*divisiblePar)) -ne ${maximum} ]; then

if [ ${maximum} -lt 0 ]; then

maximum=$((((maximum/divisiblePar)-1)*divisiblePar))

else

maximum=$((maximum/divisiblePar*divisiblePar))

fi

fi

# Nous avons besoin de générer seulement les sous-scripts de tableaux positifs,

#+ donc nous avons besoin d'un déplacement qui nous garantie des résultats positifs.

deplacement=$((0-minimum))

for ((i=${minimum}; i<=${maximum}; i+=divisiblePar)); do

reponse[i+deplacement]=0

done

# Maintenant, bouclons avec un gros nombre de fois pour voir ce que nous obtenons.

loopIt=1000 # L'auteur du script suggère 100000,

#+ mais cela prend beaucoup de temps.

for ((i=0; i<${loopIt}; ++i)); do

# Notez que nous spécifions min et max en ordre inverse ici pour s'assurer que les

#+ fonctions sont correctes dans ce cas.

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 8/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

aleatoireEntre ${max} ${min} ${divisiblePar}

# Rapporte une erreur si une réponse est inattendue.

[ ${aleatoireEntreAnswer} -lt ${min} -o ${aleatoireEntreAnswer} -gt ${max} ] \

&& echo MIN or MAX error - ${aleatoireEntreAnswer}!

[ $((aleatoireEntreAnswer%${divisiblePar})) -ne 0 ] \

&& echo DIVISIBLE BY error - ${aleatoireEntreAnswer}!

# Stocke la réponse statistiquement.

reponse[aleatoireEntreAnswer+deplacement]=$((reponse[aleatoireEntreAnswer+deplacement]+1))

done

# Vérifions les résultats.

for ((i=${minimum}; i<=${maximum}; i+=divisiblePar)); do

[ ${reponse[i+deplacement]} -eq 0 ] \

&& echo "We never got an reponse of $i." \

|| echo "${i} occurred ${reponse[i+deplacement]} times."

done

exit 0

À quel point $RANDOM est-il aléatoire ? la


meilleure façon de le tester est d'écrire un script qui enregistre
la
suite des nombres « aléatoires »
générés par $RANDOM. Faisons tourner
$RANDOM plusieurs fois...

Exemple 9.30. Lancement d'un seul dé avec RANDOM

#!/bin/bash

# À quel point RANDOM est aléatoire?

RANDOM=$$ # Réinitialise le générateur de nombres aléatoires en utilisant

#+ le PID du script.

PIPS=6 # Un dé a 6 faces.

COMPTEURMAX=600# Augmentez ceci si vous n'avez rien de mieux à faire.

compteur=0 # Compteur.

un=0 # Doit initialiser les comptes à zéro

deux=0 # car une variable non initialisée est nulle, et ne vaut pas zéro.

trois=0

quatre=0

cinq=0

six=0

Affiche_resultat ()

echo

echo "un = $un"

echo "deux = $deux"

echo "trois = $trois"

echo "quatre = $quatre"

echo "cinq = $cinq"

echo "six = $six"

echo

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 9/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

mise_a_jour_compteur()

case "$1" in

0) let "un += 1";; # Comme le dé n'a pas de "zéro", ceci correspond à 1.

1) let "deux += 1";; # Et ceci à 2, etc.

2) let "trois += 1";;


3) let "quatre += 1";;

4) let "cinq += 1";;

5) let "six += 1";;

esac

echo

while [ "$compteur" -lt "$COMPTEURMAX" ]

do

let "die1 = RANDOM % $PIPS"

mise_a_jour_compteur $die1

let "compteur += 1"

done

Affiche_resultat

exit 0

# Les scores devraient être distribués de façon égale en supposant que RANDOM

#+ soit correctement aléatoire.

# Avec $COMPTEURMAX à 600, tout devrait tourner autour de 100, plus ou moins

#+ 20.

# Gardez en tête que RANDOM est un générateur pseudo-aléatoire,

# et pas un particulièrement bon.

# Le hasard est un sujet profond et complexe.

# Des séquences "au hasard" suffisamment longues pourraient exhiber un

#+ comportement cahotique et un autre comportement non aléatoire.

# Exercice (facile):

# -----------------

# Réécrire ce script pour lancer une pièce 1000 fois.

# Les choix sont "PILE" ou "FACE".

Comme nous avons vu sur le dernier exemple, il est préférable de


réinitialiser le générateur RANDOM à
chaque fois qu'il est invoqué.
Utiliser le même germe pour RANDOM ne fera que répéter la même série de
nombres [40] (ceci reflète le
comportement de la fonction C random()).

Exemple 9.31. Réinitialiser RANDOM

#!/bin/bash

# seeding-random.sh: Utiliser la variable RANDOM.

NBMAX=25 # Combien de nombres à générer.

nombres_aleatoires ()

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 10/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

compteur=0

while [ "$compteur" -lt "$NBMAX" ]

do

nombre=$RANDOM

echo -n "$nombre "

let "compteur += 1"

done

echo; echo

RANDOM=1 # Initialiser RANDOM met en place le générateur de nombres

#+ aléatoires.

nombres_aleatoires

echo; echo

RANDOM=1 # Même élément pour RANDOM...

nombres_aleatoires # ...reproduit la même série de nombres.

# Quand est-il utile de dupliquer une série de nombres

#+ "aléatoires" ?

echo; echo

RANDOM=2 # Nouvel essai, mais avec un 'germe' différent...

nombres_aleatoires # donne une autre série...

echo; echo

# RANDOM=$$ initialise RANDOM à partir du PID du script.

# Il est aussi possible d'initialiser RANDOM à partir des commandes 'time' et

#+ 'date'.

# Un peu plus d'amusement...

SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')

# Sortie pseudo-aléatoire récupérée de /dev/urandom (fichier périphérique

#+ pseudo-aléatoire),

#+ puis convertit la ligne en nombres (octal) affichables avec "od".

#+ Finalement "awk" récupère un seul nombre pour SEED.

RANDOM=$SEED

nombres_aleatoires

echo; echo

exit 0


Note
Le pseudo fichier périphérique /dev/urandom apporte une méthode pour générer des
nombres pseudo-aléatoires bien plus « aléatoires » que la variable $RANDOM. dd
if=/dev/urandom of=fichier_cible bs=1 count=XX crée
un fichier de nombres pseudo-
aléatoires bien distribués.
Néanmoins, assigner ces nombres à une variable dans un script
nécessite un petit travail supplémentaire, tel qu'un filtrage par
l'intermédiaire de od (comme
dans l'exemple ci-dessus, dans
l'Exemple 15.14,
« Générer des nombres aléatoires de dix
chiffres »
et dans Exemple A.38,
« Tri d'insertion ») ou tel que l'utilisation de
dd (voir

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 11/12
16/11/2021 00:33 9.6. $RANDOM : générer un nombre aléatoire

l'Exemple 15.59,
« Effacer les fichiers de façon sûre ») ou même
d'envoyer via un tube
dans md5sum (voir l'Exemple 33.14,
« Un jeu de « courses de
chevaux » »).

Il existe aussi
d'autres moyens pour générer des nombres pseudo aléatoires dans
un
script. Awk
propose une façon agréable de le faire.

Exemple 9.32. Nombres pseudo-aléatoires, en
utilisant awk

#!/bin/bash

# random2.sh: Renvoie un nombre pseudo-aléatoire compris entre 0 et 1.

# Utilise la fonction rand() d'awk.

SCRIPTAWK=' { srand(); print rand() } '

# Commande(s) / paramètres passés à awk

# Notez que srand() réinitialise le générateur de nombre aléatoire de awk.

echo -n "Nombre aléatoire entre 0 et 1 = "

echo | awk "$SCRIPTAWK"

# Que se passe-t-il si vous oubliez le 'echo' ?

exit 0

# Exercices :

# ----------

# 1) En utilisant une construction boucle, affichez 10 nombres aléatoires

# différents.

# (Astuce : vous devez réinitialiser la fonction "srand()" avec une donnée

# différente à chaque tour de la boucle. Qu'arrive-t'il si vous échouez à le

# faire ?)

# 2) En utilisant un multiplicateur entier comme facteur d'échelle, générez des

# nombres aléatoires compris entre 10 et 100.

# 3) De même que l'exercice #2, ci-dessus, mais en générant des nombres

# aléatoires entiers cette fois.

La commande date tend


elle-même à générer
des séquences d'entiers pseudo-aléatoires.

[39]Un vrai « hasard », si tant est qu'il puisse exister,


peut seulement être trouvé dans certains
phénomènes naturels
compris partiellement tels que la destruction radioactive. Les
ordinateurs simulent
le hasard et les séquences générées par
ordinateur de nombres « aléatoires » sont du coup appelés
pseudo-aléatoires.

[40]
La graine d'une série de nombres pseudo-aléatoires
générés par un ordinateur peut être considérée
comme un label
d'identification. Par exemple, pensez à la série pseudo-aléatoire
avec une graine de 23
comme la série #23.

Une propriété d'une série de nombres pseudo-aléatoires est la


longueur du cycle avant qu'il ne
commence à se répéter. Un bon
générateur pseudo-aléatoire produira des séries avec de très
longs
cycles.

https://abs.traduc.org/abs-5.3-fr/ch09s06.html 12/12

Vous aimerez peut-être aussi