Jak wygenerować losową liczbę w Bash?


Odpowiedzi:


283

Zastosowanie $RANDOM. Jest to często przydatne w połączeniu z prostą arytmetyką powłoki. Na przykład, aby wygenerować losową liczbę od 1 do 10 (włącznie):

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

Rzeczywisty generator jest włączony variables.c, funkcja brand(). Starsze wersje były prostym generatorem liniowym. Wersja 4.0 bashwykorzystuje generator z cytatem do artykułu z 1985 roku, co prawdopodobnie oznacza, że ​​jest to przyzwoite źródło liczb pseudolosowych. Nie użyłbym jej do symulacji (a już na pewno nie do kryptografii), ale prawdopodobnie jest to wystarczające do podstawowych zadań skryptowych.

Jeśli robisz coś, co wymaga poważnych liczb losowych, możesz ich użyć /dev/randomlub /dev/urandomjeśli są one dostępne:

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

20
Uważaj tutaj. Chociaż jest to w porządku w mgnieniu oka, wykonywanie arytmetyki na liczbach losowych może dramatycznie wpłynąć na losowość wyniku. w przypadku $RANDOM % 108 i 9 są mierzalne (choć marginalnie) mniej prawdopodobne niż 0–7, nawet jeśli$RANDOM jest solidnym źródłem danych losowych.
dimo414

3
@ dimo414 Jestem ciekawy „marginalnie”, czy masz źródło, w którym mogę dowiedzieć się więcej na ten temat?
PascalVKooten

58
Modulując losowe dane wejściowe, „ dziurawiacz ” wyniki. Ponieważ $RANDOM„s zakres to 0-32767numery 0- 7map do 3277różnych możliwych wejść, ale 8i 9może być wytwarzany tylko 3276różne sposoby (bo 32768i 32769nie są możliwe). Jest to niewielki problem w przypadku szybkich hacków, ale oznacza, że ​​wynik nie jest jednolicie losowy. Biblioteki losowe, takie jak Java Random, oferują funkcje do prawidłowego zwracania jednolitej liczby losowej w danym zakresie, zamiast po prostu modyfikować liczbę niepodzielną.
dimo414

1
@JFSebastian bardzo prawdziwe - problem z modulo polega na tym, że może on przełamać jednolitość każdego RNG, nie tylko złych PRNG, ale dzięki za wywołanie tego.
dimo414

14
Tylko dla kontekstu podstawowe szufladkowanie dla% 10 oznacza, że ​​prawdopodobieństwo wystąpienia 8 i 9 jest o około 0,03% mniejsze niż 0–7. Jeśli twój skrypt powłoki wymaga dokładniejszych jednolitych liczb losowych niż to, to zdecydowanie użyj bardziej złożonego i odpowiedniego mechanizmu.
Nelson

70

Proszę zobaczyć $RANDOM:

$RANDOM to wewnętrzna funkcja Bash (nie stała), która zwraca pseudolosową liczbę całkowitą z zakresu 0 - 32767. Nie należy jej używać do generowania klucza szyfrowania.


1
Czy 32767ma jakieś specjalne znaczenie?
Jin Kwon,

14
@ JinKwon 32767jest 2^16 / 2 - 1górną granicą 16-bitowej liczby całkowitej ze znakiem.
Jeffrey Martinez

@ JinKwon, czy możesz wyjaśnić, dlaczego tak nie jest 2^15 - 1? Jest to równoważne, więc jestem ciekawy, czy brakuje mi jakiegoś kontekstu?
Brett Holman

11
@BrettHolman Myślę, że próbował wskazać „podpisaną” część 16-bitowej liczby całkowitej ze znakiem. 2 ^ 16 wartości, podzielone na pół dla wartości dodatnich i ujemnych.
cody

Ta odpowiedź nie odpowiada na pytanie.
brat

48

Możesz także użyć shuf (dostępne w coreutils).

shuf -i 1-100000 -n 1

Jak przechodzisz w Vars jako koniec zakresu? Mam to:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
Dodaj $varzamiast końca zakresu, jak poniżej:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Wolę tę opcję, ponieważ łatwo jest wygenerować N liczb losowych -n. Np. Wygeneruj 5 liczb losowych od 1 do 100 :shuf -i 1-100 -n 5
aerijman

O ile rozumiem, liczby nie są losowe. Jeśli określisz shuf -i 1-10 -n 10, otrzymasz wszystkie liczby od 1 do 10 dokładnie jeden. Jeśli określisz -n 15, nadal dostaniesz tylko te 10 liczb dokładnie raz. To tak naprawdę tylko tasowanie, a nie generowanie liczb losowych.
radlan

Aby uzyskać losowe liczby z zamiennikiem: -r
Geoffrey Anderson

36

Wypróbuj to ze swojej powłoki:

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

Tutaj -t dokreśla, że ​​format wyjściowy powinien być podpisany dziesiętnie; -N 1mówi, aby przeczytać jeden bajt /dev/urandom.


2
Pytanie dotyczy liczb w zakresie.
JSycamore

9
możesz usunąć spacje:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

możesz także uzyskać losową liczbę z awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 wiesz, na początku myślałem, dlaczego miałbyś chcieć to zrobić w ten sposób, ale tak naprawdę to lubię.
zelanix

1
Dziękujemy za podanie rozwiązania obejmującego siew. Nigdzie nie mogłem go znaleźć!
Zmęczony

2
+1 za wysiew. Warto wspomnieć, że srand()ziarnem jest aktualny czas procesora. Jeśli musisz podać konkretny materiał siewny, aby RNG można było powielić, użyj srand(x)gdzie xto ziarno. Cytując również z podręcznika funkcji numerycznych GNU awk, „różne implementacje awk używają wewnętrznie różnych generatorów liczb losowych”. Rezultatem jest to, że jeśli jesteś zainteresowany generowaniem rozkładu statystycznego, powinieneś spodziewać się niewielkich różnic między poszczególnymi środowiskami uruchomieniowymi na różnych platformach (wszystkie działające awklub gawk).
Cbhihe

18

Jest $ RANDOM. Nie wiem dokładnie, jak to działa. Ale to działa. Do testowania możesz:

echo $RANDOM

17

Podoba mi się ta sztuczka:

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

...


6
$ RANDOM mieści się w zakresie od 0 do 32767. Otrzymasz więcej liczb, które zaczynają się od 1, 2 lub 3, niż 4-9. Jeśli masz problem z niezrównoważoną dystrybucją, będzie to działać dobrze.
jbo5112,

2
@ jbo5112 masz całkowitą rację, a co z wyświetlaniem ostatniej cyfry? echo $ {RANDOM: 0-1} na jedną cyfrę, $ {RANDOM: 0-2} na dwie cyfry ...?
fraff

3
Jeśli użyjesz ostatnich cyfr, będzie to raczej dobre, ale będzie zawierać 0 i 00. Na pojedynczej cyfrze 0–7 pojawi się o 0,03% częściej niż 8–9. Na 2 cyfrach 0-67 pojawi się o 0,3% częściej niż 68-99. Jeśli potrzebujesz tak dobrej dystrybucji liczb losowych, mam nadzieję, że nie używasz bash. Z oryginałem: ${RANDOM:0:1}ma 67,8% szansy na 1 lub 2, ${RANDOM:0:2}ma tylko 0,03% szansy na nadanie jednocyfrowej liczby (powinno wynosić 1%), a obie mają 0,003% szansy na 0 Nadal istnieją przypadki użycia, w których jest to w porządku (np. Niespójne dane wejściowe).
jbo5112,

12

Losowa liczba od 0 do 9 włącznie.

echo $((RANDOM%10))

2
Mój zły, nie przeczytałem poprawnie strony podręcznika. $RANDOMzmienia się tylko z 0 na 32767. Powinien był powiedzieć „Losowa liczba, głównie między 1 a 3, z kilkoma skrzydłowymi”;)
David Newcomb

1
Co? Będzie to nadal od 0 do 9, chociaż prawdopodobieństwo wystąpienia 8 do 9 będzie nieco mniejsze niż od 0 do 7, jak wspomniano w innej odpowiedzi.
kini

6

Jeśli używasz systemu Linux, możesz uzyskać losową liczbę z / dev / random lub / dev / urandom. Bądź ostrożny / dev / random zablokuje się, jeśli nie będzie wystarczającej liczby losowych numerów. Jeśli potrzebujesz prędkości ponad przypadkowością, użyj / dev / urandom.

Te „pliki” zostaną wypełnione losowymi liczbami generowanymi przez system operacyjny. To zależy od implementacji / dev / random w twoim systemie, jeśli otrzymasz prawdziwe lub pseudolosowe liczby. Prawdziwe liczby losowe są generowane za pomocą szumu zebranego ze sterowników urządzeń, takich jak mysz, dysk twardy, sieć.

Możesz pobrać losowe liczby z pliku za pomocą dd


5

Wziąłem kilka z tych pomysłów i stworzyłem funkcję, która powinna działać szybko, jeśli wymaganych jest wiele liczb losowych.

dzwonienie odjest drogie, jeśli potrzebujesz wielu losowych numerów. Zamiast tego dzwonię raz i przechowuję 1024 losowe numery z / dev / urandom. Kiedy randjest wywoływany, ostatni losowy numer jest zwracany i skalowany. Następnie jest usuwany z pamięci podręcznej. Gdy pamięć podręczna jest pusta, odczytywane są kolejne 1024 liczby losowe.

Przykład:

rand 10; echo $RET

Zwraca losową liczbę w RET od 0 do 9 włącznie.

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

AKTUALIZACJA: To nie działa tak dobrze dla wszystkich N. Marnuje również losowe bity, jeśli jest używane z małą N. Zauważ, że (w tym przypadku) 32-bitowa liczba losowa ma wystarczającą entropię dla 9 liczb losowych od 0 do 9 (10 * 9 = 1 000 000 000 <= 2 * 32) możemy wyodrębnić wiele liczb losowych z każdej 32 losowej wartości źródłowej.

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

Próbowałem tego - zajęło 10 sekund 100% procesora, a następnie wydrukowałem 10 liczb, które wcale nie wyglądały losowo.
Carlo Wood,

Teraz pamiętam. Ten kod generuje 100 000 liczb losowych. Umieszcza każdy w „koszu”, aby sprawdzić, jak losowy. Jest 10 pojemników. Liczby te powinny być podobne, jeśli każda liczba losowa od 0 do 9 jest jednakowo prawdopodobna. Jeśli chcesz wydrukować każdy numer, wyślij echo $ RET po randBetween 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomwygeneruje 10 losowych 32-bitowych liczb całkowitych bez znaku oddzielonych spacjami. możesz przechowywać go w tablicy i użyć go później. twój kod wydaje się przesadą.
Ali

@Ali, OP nie określił, że chcą 32-bitowej liczby losowej o dowolnej wielkości. Ja i niektórzy inni zinterpretowaliśmy to pytanie jako zapewniające losową liczbę w zakresie. Moja funkcja rand osiąga ten cel, a także zmniejsza utratę entropii, która, jeśli zostanie wyczerpana, powoduje blokowanie programów. od on / dev / urandom zwraca tylko 2 ^ N bitowe liczby losowe, a OP musiałby wówczas przechowywać wiele wartości w tablicy, kolejno wyodrębniając je z tej tablicy i uzupełniając tę ​​tablicę. Być może możesz zakodować to jako odpowiedź i obsłużyć inne zakresy liczb losowych?
philcolbourn

@philcolbourn, masz rację co do tego, że OP nie określa, jakiego rodzaju liczb losowych chce, i to zwróciło moją uwagę. Ale on tylko zapytał: „Jak wygenerować na liczbę losową w bash?”. Chodzi mi o to, że poprosił tylko o jedną losową liczbę. Chociaż ten krytyk dotyczy również mojego poprzedniego komentarza (generującego 10 liczb losowych).
Ali,

5

Czytanie z plików specjalnych znaków / dev / random lub / dev / urandom jest właściwą drogą.

Urządzenia te zwracają prawdziwie losowe liczby podczas odczytu i mają pomóc aplikacjom w wyborze bezpiecznych kluczy do szyfrowania. Takie liczby losowe są pobierane z puli entropii, która jest generowana przez różne zdarzenia losowe. {LDD3, Jonathan Corbet, Alessandro Rubini i Greg Kroah-Hartman]

Te dwa pliki są w szczególności interfejsem do randomizacji jądra

void get_random_bytes_arch(void* buf, int nbytes)

który pobiera prawdziwie losowe bajty ze sprzętu, jeśli taka funkcja jest realizowana sprzętowo (zwykle jest), lub pobiera z puli entropii (składającej się z czasów między zdarzeniami, takimi jak przerwania myszy i klawiatury oraz inne przerwania zarejestrowane w SA_SAMPLE_RANDOM).

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

Działa, ale zapisuje niepotrzebne dane wyjściowe z ddna standardowe wyjście. Poniższe polecenie podaje tylko liczbę całkowitą, której potrzebuję. Mogę nawet uzyskać określoną liczbę losowych bitów według potrzeb, dostosowując maskę bitową podaną dla rozszerzenia arytmetycznego:

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

3

Co powiesz na:

perl -e 'print int rand 10, "\n"; '

Aby uzyskać kryptograficznie bezpieczną liczbę losową, musisz przeczytać z / dev / urandom lub użyć bibliotek Crypt :: Random.
kh

3

Może jestem trochę za późno, ale co jotz generowaniem losowej liczby w zakresie w Bash?

jot -r -p 3 1 0 1

Generuje to -rliczbę losową ( ) z dokładnością do 3 miejsc po przecinku ( -p). W tym konkretnym przypadku otrzymasz jedną liczbę od 0 do 1 ( 1 0 1). Możesz także wydrukować dane sekwencyjne. Źródłem liczby losowej, zgodnie z instrukcją, jest:

Liczby losowe są uzyskiwane przez arc4random (3), gdy nie podano nasion, i przez losowe (3), gdy podano ziarno.


Musi być zainstalowany: sudo apt install athena-jot
xerostomus

3

Oparty na świetnych odpowiedziach @Nelson, @Barun i @Robert, oto skrypt Bash, który generuje losowe liczby.

  • Może wygenerować ile cyfr chcesz.
  • każda cyfra jest generowana osobno, dzięki /dev/urandomczemu jest znacznie lepsza niż wbudowana Bash$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

Dokładnie to, o co mi chodziło.
Prometeusz

2

Wygeneruj liczbę losową z zakresu od 0 do n (16-bitowa liczba całkowita ze znakiem). Zestaw wyników w zmiennej $ RAND. Na przykład:

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

1

Losowe rozgałęzienie programu lub tak / nie; 1/0; wyjście prawda / fałsz:

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

jeśli leniwy zapamiętać 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.