Hasilkan angka acak dalam rentang tertentu

84

Setelah googling sedikit, saya tidak dapat menemukan cara sederhana untuk menggunakan perintah shell untuk menghasilkan bilangan bulat desimal acak yang termasuk dalam rentang tertentu, yaitu antara minimum dan maksimum.

Saya membaca tentang /dev/random, /dev/urandomdan $RANDOM, tetapi tidak ada yang bisa melakukan apa yang saya butuhkan.

Apakah ada perintah lain yang bermanfaat, atau cara untuk menggunakan data sebelumnya?

BowPark
sumber

Jawaban:

46

Di toolchest POSIX, Anda dapat menggunakan awk:

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

Jangan tidak menggunakannya sebagai sumber untuk membuat password atau data rahasia misalnya, seperti kebanyakan awkimplementasi, jumlah dapat dengan mudah ditebak didasarkan pada waktu yang perintah dijalankan.

Dengan banyak awkimplementasi, perintah yang dijalankan dua kali dalam detik yang sama umumnya akan memberi Anda output yang sama.

Stéphane Chazelas
sumber
1
+1 .. Seperti yang digunakan dalam skrip untuk menetapkan output ke variabel (tanpa mengenalkan karakter baris baru dan menggunakan zero padding untuk dua tempat):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher
Ini berdasarkan waktu? karena saya mendapat nilai yang sama dalam 2 kali berturut-turut ...
Shai Alon
@ShaiAlon waktu saat ini sering digunakan sebagai nilai seed default untuk srand () jadi biasanya, ya itu berdasarkan waktu, lihat kalimat Stéphane tentang itu dalam jawabannya. Anda dapat menyiasatinya dengan mengubah seed awal menjadi angka acak 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))}'. Anda bisa menggunakan $RANDOMalih-alih od ... /dev/randomtetapi kemudian nilai seed Anda dalam kisaran yang relatif kecil dari 0 hingga 32767 dan karenanya Anda mungkin akan mendapatkan pengulangan yang terlihat dalam output Anda dari waktu ke waktu.
Ed Morton
142

Anda dapat mencoba shufdari GNU coreutils:

shuf -i 1-100 -n 1
cuonglm
sumber
Ini juga berfungsi dengan Busybox shuf.
cov
Bekerja di raspbian dan GitBash juga.
nPcomp
30

mencatat

Pada BSD dan OSX Anda dapat menggunakan jot untuk mengembalikan nomor acak tunggal ( -r) dari interval minke max, inklusif.

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

Masalah distribusi

Sayangnya, jangkauan dan distribusi angka yang dihasilkan secara acak dipengaruhi oleh fakta bahwa jot menggunakan aritmatika floating point presisi ganda secara internal dan printf (3) untuk format output, yang menyebabkan masalah pembulatan dan pemotongan. Oleh karena itu, interval mindan maxdihasilkan lebih jarang seperti yang ditunjukkan:

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

Pada OS X 10.11 (El Capitan) ini tampaknya telah diperbaiki:

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

dan...

$ 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

Memecahkan masalah distribusi

Untuk versi OS X yang lebih lama, untungnya ada beberapa solusi. Salah satunya adalah menggunakan konversi integer printf (3). Satu-satunya peringatan adalah bahwa interval maksimum sekarang menjadi max+1. Dengan menggunakan format integer, kami mendapatkan distribusi yang adil di seluruh interval:

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

Solusi sempurna

Akhirnya, untuk mendapatkan gulungan dadu yang adil menggunakan solusi, kami memiliki:

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

Pekerjaan rumah ekstra

Lihat jot (1) untuk matematika berdarah dan detail format dan banyak lagi contoh.

Clint Pachl
sumber
15

The $RANDOMvariabel biasanya bukan cara yang baik untuk menghasilkan nilai acak yang baik. Output dari /dev/[u]randomkebutuhan juga harus dikonversi terlebih dahulu.

Cara yang lebih mudah adalah dengan menggunakan bahasa tingkat yang lebih tinggi, seperti misalnya python:

Untuk menghasilkan variabel integer acak antara 5 dan 10 (5 <= N <= 10), gunakan

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

Jangan gunakan ini untuk aplikasi kriptografi.

Jofel
sumber
Ini bermanfaat. Terima kasih banyak :) Seperti yang ditandatangani sering berhasil dengan Unix-> Solaris 10, utilitas GNU tidak terintegrasi secara default. Namun ini berhasil.
Kemauan
11

Anda bisa mendapatkan nomor acak urandom

head -200 /dev/urandom | cksum

Keluaran:

3310670062 52870

Untuk mengambil satu bagian dari nomor di atas.

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

Maka outputnya adalah

3310670062

zangw
sumber
head -200(atau yang setara dengan POSIX head -n 100) mengembalikan 200 baris pertama /dev/urandom. /dev/urandombukan file teks, perintah itu bisa kembali dari 200 byte (semua yang 0x0a, LF di ASCII) hingga tak terbatas (jika byte 0xa terjadi tidak pernah dikembalikan) tetapi nilai antara 45k dan 55k adalah yang paling mungkin. cksum mengembalikan angka 32bit, jadi tidak ada gunanya untuk mendapatkan lebih dari 4 byte /dev/urandom.
Stéphane Chazelas
7

Untuk menghasilkan variabel integer acak antara 5 dan 10 (termasuk keduanya), gunakan

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

% bekerja sebagai operator modulo.

Mungkin ada cara yang lebih baik untuk mengonversi variabel acak $RANDOMke rentang tertentu. Jangan gunakan ini untuk aplikasi kriptografi atau dalam kasus Anda memerlukan variabel acak yang terdistribusi secara merata (seperti untuk simulasi).

Jofel
sumber
3
Dalam banyak implementasi shell yang memiliki $ RANDOM (terutama yang lebih tua, terutama bash), itu tidak terlalu acak. Dalam kebanyakan shell, angkanya antara 0 dan 65535, sehingga setiap rentang yang lebarnya bukan kekuatan dua akan memiliki perbedaan distribusi probabilitas. (dalam hal ini, angka 5 hingga 8 akan memiliki probabilitas 10923/65536, sedangkan angka 9 dan 10 akan memiliki probabilitas 10922/65536). Semakin luas rentang, semakin besar perbedaannya.
Stéphane Chazelas
Maaf, itu 32767 bukan 65535, jadi perhitungan di atas salah (dan sebenarnya lebih buruk).
Stéphane Chazelas
@ StéphaneChazelas Saya memikirkan ini ketika saya menulis bahwa ada cara yang lebih baik ...
jofel
5

# echo $(( $RANDOM % 256 )) akan menghasilkan angka "acak" antara 0-255 dalam dialek * sh modern.

qrkourier
sumber
Tampak mirip dengan jawaban lain ini dari 2 tahun yang lalu.
Jeff Schaller
1
Ini tunduk pada masalah pigeonhole (TL; DR: beberapa hasil lebih mungkin daripada yang lain) jika, alih-alih 256, Anda menggunakan nilai yang tidak membagi 32768 secara merata.
toon81
4

Mungkin UUID(di Linux) dapat digunakan untuk mengambil nomor acak

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

Untuk mendapatkan nomor acak antara 70dan100

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

Ini akan menghasilkan angka heksadesimal 8 digit.

Tom Cooper
sumber
1

Untuk tetap sepenuhnya dalam bash dan gunakan variabel $ RANDOM tetapi hindari distribusi yang tidak merata:

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

Contoh ini akan memberikan angka acak dari 20 hingga 29.

Dari Angell
sumber
$rharus diinisialisasi ke 65535 atau nilai tinggi lainnya untuk menghindari [: -lt: unary operator expectedkesalahan.
LawrenceC
Mengoreksi inisialisasi $ r sebelum tes loop. Terima kasih @LawrenceC!
Than Angell
0

Distribusi bagus:

untuk ((i = 1; i <= 100000; i ++)) lakukan echo $ ((RANDOM% (20 - 10 + 1) + 10)); selesai | sort -n | uniq -c

nilai hitung

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

SteveK
sumber