Wie generiere ich Zufallszahlen in Bash?

235

Wie generiere ich eine Zufallszahl innerhalb eines Bereichs in Bash?

woakas
quelle
21
Wie zufällig muss es sein?
Bdonlan

Antworten:

283

Verwenden Sie $RANDOM. Es ist oft nützlich in Kombination mit einfacher Shell-Arithmetik. Zum Beispiel, um eine Zufallszahl zwischen 1 und 10 (einschließlich) zu generieren:

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

Der eigentliche Generator ist in variables.cder Funktion brand(). Ältere Versionen waren ein einfacher Lineargenerator. Version 4.0 von bashverwendet einen Generator mit einem Zitat aus einem Artikel von 1985, was vermutlich bedeutet, dass es eine anständige Quelle für Pseudozufallszahlen ist. Ich würde es nicht für eine Simulation verwenden (und schon gar nicht für Krypto), aber es ist wahrscheinlich ausreichend für grundlegende Skriptaufgaben.

Wenn Sie etwas tun, das ernsthafte Zufallszahlen erfordert, können Sie diese verwenden /dev/randomoder /dev/urandomwenn sie verfügbar sind:

$ dd if=/dev/urandom count=4 bs=1 | od -t d
Nelson
quelle
20
Sei hier vorsichtig. Während dies zur Not in Ordnung ist, kann das Rechnen mit Zufallszahlen die Zufälligkeit Ihres Ergebnisses dramatisch beeinflussen. im Fall von $RANDOM % 10sind 8 und 9 messbar (wenn auch geringfügig) weniger wahrscheinlich als 0-7, selbst wenn $RANDOMes sich um eine robuste Quelle für zufällige Daten handelt.
dimo414
3
@ dimo414 Ich bin neugierig auf "marginal", hast du eine Quelle, wo ich mehr darüber erfahren kann?
PascalVKooten
58
Indem Sie Ihre zufällige Eingabe modulieren, " stecken " Sie die Ergebnisse in eine Schublade . Da $RANDOM‚s - Bereich ist 0-32767die Zahl 0- 7Karte zu 3277verschiedenen möglichen Eingaben, aber 8und 9kann nur hergestellt werden 3276unterschiedliche Weise (da 32768und 32769sind nicht möglich). Dies ist ein kleines Problem bei schnellen Hacks, bedeutet jedoch, dass das Ergebnis nicht einheitlich zufällig ist. Zufällige Bibliotheken wie Java Randombieten Funktionen, um eine einheitliche Zufallszahl im angegebenen Bereich korrekt zurückzugeben, anstatt einfach eine nicht teilbare Zahl zu ändern.
dimo414
1
@JFSebastian sehr wahr - das Problem mit Modulo ist, dass es die Einheitlichkeit jedes RNG brechen kann , nicht nur schlechte PRNGs, sondern danke, dass Sie dies hervorgehoben haben.
dimo414
14
Nur für den Kontext bedeutet, dass die Grundfächer für% 10 bedeuten, dass 8 und 9 etwa 0,03% weniger wahrscheinlich sind als 0–7. Wenn Ihr Shell-Skript genauere einheitliche Zufallszahlen erfordert, verwenden Sie auf jeden Fall einen komplexeren und angemesseneren Mechanismus.
Nelson
70

Bitte sehen Sie $RANDOM:

$RANDOM ist eine interne Bash-Funktion (keine Konstante), die eine pseudozufällige Ganzzahl im Bereich von 0 bis 32767 zurückgibt. Sie sollte nicht zum Generieren eines Verschlüsselungsschlüssels verwendet werden.

Andrew Hare
quelle
1
Hat 32767eine besondere Bedeutung?
Jin Kwon
14
@JinKwon 32767ist 2^16 / 2 - 1die Obergrenze für eine vorzeichenbehaftete 16-Bit-Ganzzahl.
Jeffrey Martinez
@ JinKwon könntest du klarstellen, warum du es nicht sagst 2^15 - 1? Es ist gleichwertig, also bin ich nur neugierig, ob mir ein Kontext fehlt?
Brett Holman
11
@BrettHolman Ich glaube, er hat versucht, auf den "vorzeichenbehafteten" Teil der vorzeichenbehafteten 16-Bit-Ganzzahl hinzuweisen. 2 ^ 16 Werte, halbiert für positive und negative Ints.
Cody
Diese Antwort beantwortet die Frage nicht.
Göre
48

Sie können auch shuf verwenden (erhältlich in coreutils).

shuf -i 1-100000 -n 1
Knipwim
quelle
Wie übergeben Sie vars als Bereichsende? Ich habe folgendes:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus
1
Fügen Sie ein $varanstelle des Bereichsende hinzu, wie var=100 && shuf -i 1-${var} -n 1
folgt
2
Ich bevorzuge diese Option, da es einfach ist, N Zufallszahlen mit zu generieren -n. ZB Generiere 5 Zufallszahlen zwischen 1 und 100 :shuf -i 1-100 -n 5
Aerijman
Soweit ich weiß, sind die Zahlen nicht zufällig. Wenn Sie angeben, erhalten shuf -i 1-10 -n 10Sie alle Zahlen von 1 bis 10 genau eins. Wenn Sie angeben, erhalten -n 15Sie immer noch nur einmal genau diese 10 Zahlen. Das ist wirklich nur Mischen, keine Zufallszahlen erzeugen.
Radlan
Um Zufallszahlen mit Ersatz zu erhalten: -r
Geoffrey Anderson
36

Versuchen Sie dies aus Ihrer Shell:

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

Gibt hier an, -t ddass das Ausgabeformat dezimal signiert werden soll. -N 1sagt, ein Byte von zu lesen /dev/urandom.

Barun
quelle
2
Die Frage fragt nach Zahlen in einem Bereich.
JSycamore
9
Sie können Leerzeichen entfernen:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert
23

Sie können auch eine Zufallszahl von awk erhalten

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'
Ghostdog74
quelle
1
+1 weißt du, zuerst dachte ich mir, warum würdest du es jemals so machen wollen, aber eigentlich gefällt es mir ganz gut.
Zelanix
1
Vielen Dank für die Bereitstellung einer Lösung, die das Säen umfasst. Ich konnte es nirgendwo finden!
müde
2
+1 für die Aussaat. Es könnte erwähnenswert sein, dass srand()der Startwert die aktuelle CPU-Zeit ist. Wenn Sie einen bestimmten Startwert angeben müssen, damit RNG dupliziert werden kann, verwenden Sie, srand(x)wo xsich der Startwert befindet. Aus dem numerischen Funktionshandbuch von GNU awk geht hervor, dass "verschiedene awk-Implementierungen intern unterschiedliche Zufallszahlengeneratoren verwenden". Das Ergebnis ist, dass Sie, wenn Sie an der Erstellung einer statistischen Verteilung interessiert sind, geringfügige Abweichungen von einer Laufzeit zur nächsten auf verschiedenen Plattformen (alle ausgeführt awkoder gawk) erwarten sollten .
Cbhihe
18

Es gibt $ RANDOM. Ich weiß nicht genau, wie es funktioniert. Aber es funktioniert. Zum Testen können Sie Folgendes tun:

echo $RANDOM
Antoine Claval
quelle
17

Ich mag diesen Trick:

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

...

fraff
quelle
6
$ RANDOM liegt im Bereich von 0 bis 32767. Sie erhalten mehr Zahlen, die mit 1, 2 oder 3 beginnen, als 4-9. Wenn Sie mit einer unausgeglichenen Verteilung einverstanden sind, funktioniert dies einwandfrei.
jbo5112
2
@ jbo5112 du hast vollkommen recht, was ist mit der Anzeige der letzten Ziffer? Echo $ {RANDOM: 0-1} für eine Ziffer, $ {RANDOM: 0-2} für zwei Ziffern ...?
Fraff
3
Wenn Sie die letzte (n) Ziffer (n) verwenden, ist diese ziemlich gut, enthält jedoch Nullen und 00er. Auf einer einzelnen Ziffer tritt 0-7 0,03% häufiger auf als 8-9. Bei 2 Ziffern tritt 0-67 0,3% häufiger auf als 68-99. Wenn Sie eine so gute Zufallszahlenverteilung benötigen, verwenden Sie hoffentlich keine Bash. Mit dem Original: ${RANDOM:0:1}hat eine 67,8% ige Chance, Ihnen eine 1 oder 2 zu geben, hat ${RANDOM:0:2}nur eine 0,03% ige Chance, Ihnen eine einstellige Zahl zu geben (sollte 1% sein), und beide haben eine 0,003% ige Chance, Ihnen eine 0 zu geben Es gibt immer noch Anwendungsfälle, in denen dies in Ordnung ist (z. B. nicht konsistente Eingabe).
jbo5112
12

Zufallszahl zwischen 0 und 9 einschließlich.

echo $((RANDOM%10))
David Newcomb
quelle
2
Mein schlechtes, habe die Manpage nicht richtig gelesen. $RANDOMgeht nur von 0 bis 32767. Es sollte gesagt haben "Zufallszahl meist zwischen 1 und 3, mit ein paar Flügelspielern";)
David Newcomb
1
Was? Es wird immer noch zwischen 0 und 9 liegen, obwohl 8 und 9 etwas weniger wahrscheinlich sind als 0 bis 7, wie in einer anderen Antwort erwähnt.
Kini
6

Wenn Sie ein Linux-System verwenden, können Sie eine Zufallszahl aus / dev / random oder / dev / urandom abrufen. Seien Sie vorsichtig, wenn / dev / random blockiert, wenn nicht genügend Zufallszahlen verfügbar sind. Wenn Sie Geschwindigkeit über Zufälligkeit benötigen, verwenden Sie / dev / urandom.

Diese "Dateien" werden mit Zufallszahlen gefüllt, die vom Betriebssystem generiert werden. Es hängt von der Implementierung von / dev / random auf Ihrem System ab, ob Sie echte oder Pseudozufallszahlen erhalten. Echte Zufallszahlen werden mithilfe von Rauschen generiert, das von Gerätetreibern wie Maus, Festplatte und Netzwerk erfasst wird.

Mit dd können Sie Zufallszahlen aus der Datei abrufen

Janusz
quelle
5

Ich habe einige dieser Ideen aufgegriffen und eine Funktion entwickelt, die schnell ausgeführt werden sollte, wenn viele Zufallszahlen erforderlich sind.

Anrufen odist teuer, wenn Sie viele Zufallszahlen benötigen. Stattdessen rufe ich es einmal auf und speichere 1024 Zufallszahlen aus / dev / urandom. Wannrand Aufruf wird die letzte Zufallszahl zurückgegeben und skaliert. Es wird dann aus dem Cache entfernt. Wenn der Cache leer ist, werden weitere 1024 Zufallszahlen gelesen.

Beispiel:

rand 10; echo $RET

Gibt eine Zufallszahl in RET zwischen 0 und 9 einschließlich zurück.

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

UPDATE: Das funktioniert nicht für alle N. Es verschwendet auch zufällige Bits, wenn es mit kleinem N verwendet wird. Beachten Sie, dass (in diesem Fall) eine 32-Bit-Zufallszahl genug Entropie für 9 Zufallszahlen zwischen 0 und 9 (10 * 9) hat = 1.000.000.000 <= 2 * 32) Wir können mehrere Zufallszahlen aus jedem 32 zufälligen Quellwert extrahieren.

#!/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
philcolbourn
quelle
Ich habe es versucht - es dauerte 10 Sekunden bei 100% CPU und dann wurden 10 Zahlen gedruckt, die überhaupt nicht zufällig aussahen.
Carlo Wood
Ich erinnere mich jetzt. Dieser Code generiert 100.000 Zufallszahlen. Es legt jeden in einen "Behälter", um zu sehen, wie zufällig es ist. Es gibt 10 Behälter. Diese Zahlen sollten ähnlich sein, wenn jede Zufallszahl zwischen 0 und 9 gleich wahrscheinlich ist. Wenn Sie jede Nummer drucken möchten, geben Sie $ RET nach randBetween 10 zurück.
philcolbourn
od -An -tu4 -N40 /dev/urandomgeneriert 10 zufällige vorzeichenlose 32-Bit-Ganzzahlen, die durch Leerzeichen getrennt sind. Sie können es in einem Array speichern und anschließend verwenden. Ihr Code scheint ein Overkill zu sein.
Ali
@Ali, OP hat nicht angegeben, dass sie 32 Bit oder eine andere Zufallszahl wollen. Ich und einige andere interpretierten diese Frage als Zufallszahl innerhalb eines Bereichs. Meine Rand-Funktion erreicht dieses Ziel und reduziert auch den Entropieverlust, der bei Erschöpfung dazu führt, dass Programme blockieren. od on / dev / urandom gibt nur 2 ^ N-Bit-Zufallszahlen zurück, und OP müsste dann mehrere Werte in einem Array speichern, diese nacheinander aus diesem Array extrahieren und dieses Array auffüllen. Vielleicht können Sie dies als Antwort codieren und andere Zufallszahlenbereiche behandeln?
Philcolbourn
@philcolbourn, Sie haben Recht damit, dass das OP nicht angibt, welche Art von Zufallszahl er möchte, und es hat meine Aufmerksamkeit verfehlt. Aber er fragte nur: "Wie generiert man eine Zufallszahl in Bash?". Mein Punkt ist, dass er nur nach einer Zufallszahl gefragt hat. Dieser Kritiker gilt jedoch auch für meinen vorherigen Kommentar (der 10 Zufallszahlen generiert ).
Ali
5

Das Lesen aus speziellen Dateien mit / dev / random- oder / dev / urandom-Zeichen ist der richtige Weg.

Diese Geräte geben beim Lesen wirklich zufällige Zahlen zurück und sollen der Anwendungssoftware bei der Auswahl sicherer Schlüssel für die Verschlüsselung helfen. Solche Zufallszahlen werden aus einem Entropiepool extrahiert, der durch verschiedene Zufallsereignisse bereitgestellt wird. {LDD3, Jonathan Corbet, Alessandro Rubini und Greg Kroah-Hartman]

Diese beiden Dateien sind insbesondere eine Schnittstelle zur Kernel-Randomisierung

void get_random_bytes_arch(void* buf, int nbytes)

Dies zieht wirklich zufällige Bytes von der Hardware, wenn diese Funktion von der implementierten Hardware ist (normalerweise) oder aus dem Entropiepool (bestehend aus Zeitabläufen zwischen Ereignissen wie Maus- und Tastatur-Interrupts und anderen Interrupts, die bei SA_SAMPLE_RANDOM registriert sind).

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

Dies funktioniert, schreibt jedoch nicht benötigte Ausgaben von ddin stdout. Der folgende Befehl gibt nur die Ganzzahl an, die ich benötige. Ich kann sogar eine bestimmte Anzahl von Zufallsbits erhalten, wenn ich die Bitmaske für die arithmetische Erweiterung anpasse:

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

Wie wäre es mit:

perl -e 'print int rand 10, "\n"; '
kh
quelle
Für eine kryptografisch sichere Zufallszahl müssen Sie aus / dev / urandom lesen oder die Crypt :: Random-Bibliotheken verwenden.
kh
3

Vielleicht bin ich etwas zu spät, aber was ist mit jotder Generierung einer Zufallszahl innerhalb eines Bereichs in Bash?

jot -r -p 3 1 0 1

Dies erzeugt eine Zufallszahl ( -r) mit einer Genauigkeit von 3 Dezimalstellen ( -p). In diesem speziellen Fall erhalten Sie eine Zahl zwischen 0 und 1 ( 1 0 1). Sie können auch sequentielle Daten drucken. Die Quelle der Zufallszahl ist laut Handbuch:

Zufallszahlen werden durch arc4random (3) erhalten, wenn kein Startwert angegeben ist, und durch Zufallszahlen (3), wenn ein Startwert angegeben wird.

Vinicius Placco
quelle
Muss installiert werden: sudo apt installiert ATHENA-jot
xerostomus
3

Basierend auf den großartigen Antworten von @Nelson, @Barun und @Robert ist hier ein Bash-Skript, das Zufallszahlen generiert.

  • Kann generieren, wie viele Ziffern Sie möchten.
  • Jede Ziffer wird separat generiert, /dev/urandomwas viel besser ist als die von Bash integrierte$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
Euphorie
quelle
Genau das, wonach ich gesucht habe.
Prometheus vor
2

Generieren Sie eine Zufallszahl im Bereich von 0 bis n (vorzeichenbehaftete 16-Bit-Ganzzahl). Ergebnis in $ RAND Variable gesetzt. Beispielsweise:

#!/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
Pavlo Bashynskyi
quelle
1

Zufällige Verzweigung eines Programms oder ja / nein; 1/0; wahre / falsche Ausgabe:

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

von wenn Sie faul sind, sich an 16383 zu erinnern:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
Xerostomus
quelle