Generieren Sie Zufallszahlen in einem bestimmten Bereich

84

Nachdem ich ein bisschen gegoogelt hatte, konnte ich keine einfache Möglichkeit finden, mit einem Shell-Befehl eine zufällige dezimale Ganzzahl zu generieren, die in einem bestimmten Bereich zwischen einem Minimum und einem Maximum liegt.

Ich habe gelesen /dev/random, /dev/urandomund $RANDOM, aber keiner von ihnen kann tun , was ich brauche.

Gibt es einen anderen nützlichen Befehl oder eine Möglichkeit, die vorherigen Daten zu verwenden?

BowPark
quelle

Antworten:

45

Im POSIX-Toolchest können Sie Folgendes verwenden awk:

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

Verwenden Sie dies beispielsweise nicht als Quelle zum Generieren von Kennwörtern oder geheimen Daten, da bei den meisten awkImplementierungen die Anzahl anhand des Zeitpunkts, zu dem der Befehl ausgeführt wurde, leicht geschätzt werden kann.

Bei vielen awkImplementierungen gibt dieser Befehl, der zweimal in derselben Sekunde ausgeführt wird, im Allgemeinen dieselbe Ausgabe aus.

Stéphane Chazelas
quelle
1
+1 .. Wie in einem Skript verwendet, um einer Variablen eine Ausgabe zuzuweisen (ohne ein Zeilenumbruchzeichen einzuführen und mit Nullen für zwei Stellen aufzufüllen):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher
Es basiert auf der Zeit? da ich den gleichen Wert in 2 aufeinander folgenden Läufen bekam ...
Shai Alon
@ShaiAlon Die aktuelle Zeit wird oft als Standard-Startwert für srand () verwendet. In der Regel basiert sie auf der Zeit. Siehe dazu Stéphanes Satz in seiner Antwort. Sie können das umgehen, indem Sie den Anfangsstartwert mit in eine Zufallszahl ändern 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))}'. Sie könnten $RANDOManstelle von verwenden, od ... /dev/randomaber dann liegt Ihr Startwert im relativ kleinen Bereich von 0 bis 32767, und Sie werden wahrscheinlich im Laufe der Zeit spürbare Wiederholungen in Ihrer Ausgabe erhalten.
Ed Morton
141

Sie können es mit shufGNU coreutils versuchen :

shuf -i 1-100 -n 1
cuonglm
quelle
Dies funktioniert auch mit Busybox shuf.
30.
Funktioniert auch in Raspbian und GitBash.
nPcomp
30

jot

Unter BSD und OSX können Sie jot verwenden , um eine einzelne Zufallszahl ( -r) aus dem Intervall minbis maxeinschließlich zurückzugeben.

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

Verteilungsproblem

Unglücklicherweise wird der Bereich und die Verteilung zufällig generierter Zahlen durch die Tatsache beeinflusst, dass jot intern Gleitkomma-Arithmetik mit doppelter Genauigkeit und printf (3) für das Ausgabeformat verwendet, was zu Rundungs- und Kürzungsproblemen führt. Daher werden die Intervalle minund maxweniger häufig generiert, wie gezeigt:

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

Unter OS X 10.11 (El Capitan) scheint dies behoben worden zu sein:

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

und...

$ 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

Lösen des Verteilungsproblems

Für ältere Versionen von OS X gibt es glücklicherweise mehrere Problemumgehungen. Eine besteht darin, die Ganzzahlkonvertierung printf (3) zu verwenden. Die einzige Einschränkung ist, dass das Intervallmaximum jetzt wird max+1. Durch die Verwendung der Ganzzahlformatierung erhalten wir eine gerechte Verteilung über das gesamte Intervall:

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

Die perfekte Lösung

Um einen fairen Würfelwurf mit dem Workaround zu erzielen, haben wir:

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

Zusätzliche Hausaufgaben

In jot (1) finden Sie ausführliche Informationen zu Mathematik und Formatierung sowie viele weitere Beispiele.

Clint Pachl
quelle
15

Die $RANDOMVariable ist normalerweise kein guter Weg, um gute Zufallswerte zu generieren. Die Ausgabe von /dev/[u]randommuss auch zuerst konvertiert werden.

Eine einfachere Möglichkeit ist die Verwendung von höheren Sprachen, wie z. B. Python:

Verwenden Sie, um eine zufällige Ganzzahlvariable zwischen 5 und 10 (5 <= N <= 10) zu generieren

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

Verwenden Sie dies nicht für kryptografische Anwendungen.

Jofel
quelle
Das war nützlich. Vielen Dank :) Wie schon oft mit Unix-> Solaris 10 signiert, sind die GNU-Utils standardmäßig nicht integriert. Das hat aber geklappt.
Propatience
11

Sie können die Zufallszahl durch bekommen urandom

head -200 /dev/urandom | cksum

Ausgabe:

3310670062 52870

So rufen Sie den einen Teil der obigen Nummer ab.

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

Dann ist die Ausgabe

3310670062

zangw
quelle
head -200(oder sein POSIX-Äquivalent head -n 100) gibt die ersten 200 Zeilen von zurück /dev/urandom. /dev/urandomIst keine Textdatei, kann dieser Befehl von 200 Byte (alle 0x0a, LF in ASCII) bis unendlich (wenn das 0xa-Byte nie zurückgegeben wird) zurückgegeben werden, aber Werte zwischen 45k und 55k sind am wahrscheinlichsten. cksum gibt eine 32-Bit-Zahl zurück, daher ist es sinnlos, mehr als 4 Bytes abzurufen /dev/urandom.
Stéphane Chazelas
7

Verwenden Sie, um eine zufällige Ganzzahlvariable zwischen 5 und 10 (einschließlich beider) zu generieren

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

% arbeitet als Modulo-Operator.

Es gibt wahrscheinlich bessere Möglichkeiten, die Zufallsvariable $RANDOMin einen bestimmten Bereich umzuwandeln . Verwenden Sie dies nicht für kryptografische Anwendungen oder in Fällen, in denen Sie echte, gleichverteilte Zufallsvariablen benötigen (wie für Simulationen).

Jofel
quelle
3
In vielen Shell-Implementierungen mit $ RANDOM (insbesondere älteren, insbesondere bash) ist dies nicht sehr zufällig. In den meisten Shells liegt die Zahl zwischen 0 und 65535. Daher weist jeder Bereich, dessen Breite keine Zweierpotenz ist, eine Wahrscheinlichkeitsverteilungsdiskrepanz auf. (In diesem Fall haben die Zahlen 5 bis 8 eine Wahrscheinlichkeit von 10923/65536, während 9 und 10 eine Wahrscheinlichkeit von 10922/65536 haben). Je breiter der Bereich, desto größer die Diskrepanz.
Stéphane Chazelas
Entschuldigung, das ist 32767, nicht 65535. Die obige Berechnung ist also falsch (und es ist tatsächlich schlimmer).
Stéphane Chazelas
@ StéphaneChazelas Ich
dachte
5

# echo $(( $RANDOM % 256 )) erzeugt eine "zufällige" Zahl zwischen 0 und 255 in modernen * sh Dialekten.

qrkourier
quelle
Sieht dieser anderen Antwort von vor 2 Jahren ähnlich .
Jeff Schaller
1
Dies unterliegt dem Pigeonhole-Problem (TL; DR: Einige Ergebnisse sind wahrscheinlicher als andere), wenn Sie anstelle von 256 einen Wert verwenden, der 32768 nicht gleichmäßig teilt.
Toon81
4

Vielleicht kann die UUID(unter Linux) verwendet werden, um die Zufallszahl abzurufen

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

Um die Zufallszahl zwischen 70und zu erhalten100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid
zangw
quelle
3
cat /dev/urandom | tr -dc 'a-fA-F0-9' | fold -w 8 | head -n 1

Dadurch wird eine 8-stellige Hexadezimalzahl generiert.

Tom Cooper
quelle
1

Um vollständig in bash zu bleiben und die Variable $ RANDOM zu verwenden, aber die ungleichmäßige Verteilung zu vermeiden:

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

Dieses Beispiel würde Zufallszahlen von 20 bis 29 liefern.

Als Angell
quelle
$rsollte auf 65535 oder einen anderen hohen Wert initialisiert werden, um einen [: -lt: unary operator expectedFehler zu vermeiden .
LawrenceC
Die $ r-Initialisierung vor dem Schleifentest wurde korrigiert. Vielen Dank @LawrenceC!
Als Angell
0

Verteilung ist gut:

für ((i = 1; i <= 100000; i ++)) mache ein Echo von $ ((RANDOM% (20 - 10 + 1) + 10)); fertig | sort -n | uniq -c

Zählwert

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

SteveK
quelle