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?




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.


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

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.


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.


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


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).


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


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

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

Dadurch wird eine 8-stellige Hexadezimalzahl generiert.


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.


$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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.