Generuj liczby losowe w określonym zakresie

84

Po trochę googlowaniu nie mogłem znaleźć prostego sposobu na użycie polecenia powłoki do wygenerowania losowej liczby całkowitej dziesiętnej zawartej w określonym zakresie, tj. Między minimum a maksimum.

Czytałem o /dev/random, /dev/urandoma $RANDOM, ale żaden z nich nie może zrobić to, czego potrzebuję.

Czy istnieje inne przydatne polecenie lub sposób na wykorzystanie poprzednich danych?

BowPark
źródło

Odpowiedzi:

45

W zestawie narzędzi POSIX możesz użyć awk:

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

Czy nie używać tego jako źródło do generowania haseł lub tajne dane na przykład, jak w większości awkimplementacji, numer może się łatwo domyślić na podstawie czasu, że rozkaz został uruchomiony.

W przypadku wielu awkimplementacji to polecenie uruchomione dwukrotnie w tej samej sekundzie generalnie daje takie same wyniki.

Stéphane Chazelas
źródło
1
+1 .. Stosowany w skrypcie do przypisywania danych wyjściowych do zmiennej (bez wprowadzania znaku nowej linii i stosowania zerowania dla dwóch miejsc):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher
To zależy od czasu? ponieważ dostałem tę samą wartość w 2 kolejnych biegach ...
Shai Alon
@ShaiAlon bieżący czas jest często używany jako domyślna wartość początkowa dla srand (), więc zazwyczaj tak, jest oparty na czasie, zobacz zdanie Stéphane'a na ten temat w jego odpowiedzi. Możesz obejść ten problem, zmieniając początkowe ziarno na losową liczbę za pomocą 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))}'. Możesz użyć $RANDOMzamiast, od ... /dev/randomale wtedy twoja wartość początkowa jest w stosunkowo małym zakresie od 0 do 32767, więc prawdopodobnie dostaniesz zauważalne powtórzenia w swoim wyniku z czasem.
Ed Morton,
141

Możesz wypróbować shufz GNU coreutils:

shuf -i 1-100 -n 1
Cuonglm
źródło
Działa to również z Busybox shuf.
cov
Działa również w Raspbian i GitBash.
nPcomp
30

krztyna

W BSD i OSX możesz użyć jot, aby zwrócić pojedynczą -rliczbę losową ( ) z przedziału mindo maxwłącznie.

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

Problem z dystrybucją

Niestety na zasięg i rozkład liczb generowanych losowo ma wpływ fakt, że jot używa arytmetyki zmiennoprzecinkowej podwójnej precyzji wewnętrznie i printf (3) do formatu wyjściowego, co powoduje problemy z zaokrąglaniem i obcinaniem. Dlatego interwały mini maxsą generowane rzadziej, jak pokazano:

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

W OS X 10.11 (El Capitan) wydaje się, że zostało to naprawione:

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

i...

$ 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

Rozwiązanie problemu dystrybucji

Na starsze wersje OS X na szczęście istnieje kilka obejść. Jednym z nich jest użycie konwersji liczb całkowitych printf (3). Jedynym zastrzeżeniem jest to, że teraz staje się maksimum interwału max+1. Korzystając z formatowania liczb całkowitych, uzyskujemy sprawiedliwy rozkład w całym przedziale czasowym:

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

Idealne rozwiązanie

Wreszcie, aby uzyskać rzetelny rzut kostkami za pomocą obejścia, mamy:

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

Dodatkowa praca domowa

Zobacz jot (1), aby poznać szczegóły matematyki i formatowania oraz wiele innych przykładów.

Clint Pachl
źródło
15

$RANDOMZmienna zazwyczaj nie jest dobrym sposobem na dobre wartości generowanych losowo. W /dev/[u]randompierwszej kolejności należy również przekonwertować wynik zapotrzebowania.

Łatwiejszym sposobem jest użycie języków wyższego poziomu, takich jak np. Python:

Aby wygenerować losową zmienną całkowitą z przedziału od 5 do 10 (5 <= N <= 10), użyj

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

Nie używaj tego do aplikacji kryptograficznych.

Jofel
źródło
To było przydatne. Dziękuję bardzo :) Jak często podpisywano, współpracował z Unix-> Solaris 10, narzędzia GNU nie są domyślnie zintegrowane. To działało jednak.
propatience
11

Możesz uzyskać losową liczbę poprzez urandom

head -200 /dev/urandom | cksum

Wynik:

3310670062 52870

Aby pobrać jedną część powyższego numeru.

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

Następnie wyjście jest

3310670062

zangw
źródło
head -200(lub jego odpowiednik POSIX head -n 100) zwraca pierwszych 200 linii z /dev/urandom. /dev/urandomnie jest plikiem tekstowym, to polecenie może wrócić z 200 bajtów (wszystkie 0x0a, LF w ASCII) do nieskończoności (jeśli bajt 0xa nigdy nie zostanie zwrócony), ale najbardziej prawdopodobne są wartości od 45k do 55k. cksum zwraca liczbę 32-bitową, więc nie ma sensu uzyskiwać więcej niż 4 bajty /dev/urandom.
Stéphane Chazelas
7

Aby wygenerować losową zmienną całkowitą z przedziału od 5 do 10 (włączając obie), użyj

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

% działa jako operator modulo.

Prawdopodobnie istnieją lepsze sposoby konwersji zmiennej losowej $RANDOMna określony zakres. Nie używaj tego do aplikacji kryptograficznych lub w przypadkach, gdy potrzebujesz prawdziwych, losowo rozmieszczonych zmiennych losowych (np. Do symulacji).

Jofel
źródło
3
W wielu implementacjach powłoki, które mają $ RANDOM (szczególnie starsze, szczególnie bash), nie jest to zbyt losowe. W większości powłok liczba ta wynosi od 0 do 65535, więc każdy zakres, którego szerokość nie jest potęgą dwóch, będzie miał rozbieżność w rozkładzie prawdopodobieństwa. (w tym przypadku liczby od 5 do 8 będą miały prawdopodobieństwo 10923/65536, podczas gdy liczby 9 i 10 będą miały prawdopodobieństwo 10922/65536). Im szerszy zakres, tym większa rozbieżność.
Stéphane Chazelas
Przykro nam, to 32767, a nie 65535, więc powyższe obliczenia są nieprawidłowe (i faktycznie gorsze).
Stéphane Chazelas
@ StéphaneChazelas Miałem to na uwadze, ponieważ napisałem, że są lepsze sposoby ...
jofel
5

# echo $(( $RANDOM % 256 )) stworzy „losową” liczbę od 0 do 255 w nowoczesnych dialektach * sh.

qrkourier
źródło
Wygląda podobnie do tej innej odpowiedzi sprzed 2 lat.
Jeff Schaller
1
Jest to związane z problemem szufladki (TL; DR: niektóre wyniki są bardziej prawdopodobne niż inne), jeśli zamiast 256 użyjesz wartości, która nie dzieli równo 32768.
toon81,
4

Być może UUID(w systemie Linux) można użyć losowej liczby

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

Aby uzyskać losową liczbę między 70a100

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

Spowoduje to wygenerowanie 8-cyfrowej liczby szesnastkowej.

Tom Cooper
źródło
1

Aby pozostać całkowicie w bashu i użyć zmiennej $ RANDOM, ale unikać nierównomiernego rozkładu:

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

Ten przykład podaje losowe liczby od 20 do 29.

Niż Angell
źródło
$rnależy zainicjować na 65535 lub inną wysoką wartość, aby uniknąć [: -lt: unary operator expectedbłędu.
LawrenceC
Poprawiono inicjalizację $ r przed testem pętli. Dzięki @LawrenceC!
Niż Angell
0

Dystrybucja jest dobra:

dla ((i = 1; i <= 100000; i ++)) wykonaj echo $ ((RANDOM% (20-10 + 1) + 10)); zrobione | sort -n | uniq -c

policzyć wartość

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

SteveK
źródło