Saya telah bertanya-tanya apa yang akan menjadi cara terbaik untuk mendapatkan keacakan yang baik di bash, yaitu, apa yang akan menjadi prosedur untuk mendapatkan bilangan bulat positif acak antara MIN
dan MAX
sedemikian rupa sehingga
- Kisarannya bisa besar secara sewenang-wenang (atau setidaknya, katakanlah, hingga 2 32 -1);
- Nilai didistribusikan secara seragam (yaitu, tidak ada bias);
- Itu efisien.
Cara efisien untuk mendapatkan keacakan dalam bash adalah dengan menggunakan $RANDOM
variabel. Namun, ini hanya sampel nilai antara 0 dan 2 15 -1, yang mungkin tidak cukup besar untuk semua tujuan. Orang biasanya menggunakan modulo untuk membawanya ke kisaran yang mereka inginkan, misalnya,
MIN=0
MAX=12345
rnd=$(( $RANDOM % ($MAX + 1 - $MIN) + $MIN ))
Ini, selain itu, menciptakan bias kecuali $MAX
terjadi untuk membagi 2 15 -1 = 32767. Misalnya, jika $MIN
adalah 0 dan $MAX
adalah 9, maka nilai-nilai 0 sampai 7 sedikit lebih mungkin daripada nilai 8 dan 9, karena $RANDOM
tidak akan pernah 32768 atau 32769. Bias ini semakin memburuk dengan meningkatnya jangkauan, misalnya, jika $MIN
adalah 0 dan $MAX
adalah 9999, maka angka 0 sampai 2767 memiliki probabilitas 4 / 32767 , sedangkan nomor 2768 melalui 9999 hanya memiliki probabilitas 3 / 32767 .
Jadi sementara metode di atas memenuhi syarat 3, itu tidak memenuhi ketentuan 1 dan 2.
Metode terbaik yang saya buat sejauh ini dalam mencoba memenuhi kondisi 1 dan 2 adalah menggunakan /dev/urandom
sebagai berikut:
MIN=0
MAX=1234567890
while
rnd=$(cat /dev/urandom | tr -dc 0-9 | fold -w${#MAX} | head -1 | sed 's/^0*//;')
[ -z $rnd ] && rnd=0
(( $rnd < $MIN || $rnd > $MAX ))
do :
done
Pada dasarnya, kumpulkan saja keacakan dari /dev/urandom
(mungkin dapat dipertimbangkan untuk digunakan /dev/random
sebagai gantinya jika generator nomor pseudorandom acak yang kuat secara kriptografi diinginkan, dan jika Anda memiliki banyak waktu, atau mungkin juga generator nomor acak perangkat keras), hapus setiap karakter yang bukan digit desimal, lipat output dengan panjang $MAX
dan memotong 0 yang memimpin. Jika kita kebetulan hanya mendapatkan 0 maka $rnd
kosong, maka dalam hal ini diatur rnd
ke 0
. Periksa apakah hasilnya di luar jangkauan kami dan jika demikian, ulangi. Saya memaksa "tubuh" loop sementara ke penjaga di sini untuk memaksa eksekusi tubuh setidaknya sekali, dalam semangat meniru do ... while
loop, karena rnd
tidak ditentukan untuk memulai.
Saya pikir saya memenuhi persyaratan 1 dan 2 di sini, tapi sekarang saya mengacaukan kondisi 3. Ini agak lambat. Memakan waktu sekitar satu detik atau lebih (sepersepuluh detik saat saya beruntung). Sebenarnya, loop bahkan tidak dijamin untuk berakhir (walaupun probabilitas pemutusan konvergen menjadi 1 seiring dengan meningkatnya waktu).
Apakah ada cara yang efisien untuk mendapatkan bilangan bulat acak yang tidak bias, dalam kisaran yang ditentukan sebelumnya dan berpotensi besar, dalam bash? (Saya akan terus menyelidiki ketika waktu memungkinkan, tetapi sementara itu saya pikir seseorang di sini mungkin punya ide keren!)
Daftar Jawaban
Gagasan paling mendasar (dan karenanya portabel) adalah untuk menghasilkan bitstring acak cukup lama. Ada berbagai cara menghasilkan bitstring acak, baik menggunakan
$RANDOM
variabel bawaan bash atau menggunakanod
dan/dev/urandom
(atau/dev/random
). Jika angka acak lebih besar dari$MAX
, mulai dari awal.Atau, dimungkinkan untuk menggunakan alat eksternal.
- Solusi Perl
- Pro: cukup portabel, sederhana, fleksibel
- Kontra: bukan untuk angka yang sangat besar di atas 2 32 -1
- Solusi Python
- Pro: sederhana, fleksibel, bekerja bahkan untuk jumlah besar
- Contra: kurang portabel
- Solusi zsh
- Pro: bagus untuk orang yang menggunakan zsh
- Contra: bahkan mungkin lebih portabel
- Solusi Perl
sumber
rand=$(command)
dilakukan jikacommand
mengembalikan iteger yang memenuhi persyaratan Anda?dd if=/dev/urandom 2>/dev/null
dan menyalurkan melaluiod -t d
(menghindari jalan memutar melalui base64), tetapi tidak jelas bagi saya bagaimana konversi terjadi dan apakah itu memang tidak bias. Jika Anda dapat memperluas ide Anda menjadi skrip yang efisien dan berfungsi dan menjelaskan mengapa tidak ada bias, itu akan menjadi jawaban yang bagus. :)python
atauperl
atau bahasa favorit Anda, tetapi ini tidak tersedia di mana-mana. Saya lebih suka sesuatu yang lebih portabel. Yah,awk
fungsi acak akan baik-baik saja, kurasa. Tetapi semakin portabel, semakin baik :)perl -e 'print int(rand(2**32-1))');
. Itu sangat sangat portabel dan akan sangat cepat. Awk tidak akan memotongnya karena sebagian besar implementasi dimulai dari seed yang sama. Jadi Anda mendapatkan nomor acak yang sama pada putaran berikutnya. Itu hanya berubah dalam jangka yang sama.Jawaban:
Saya melihat metode lain yang menarik dari sini .
Yang ini juga tampaknya menjadi pilihan yang bagus. Bunyinya 4 byte dari perangkat acak dan memformatnya sebagai integer tak bertanda antara
0
dan2^32-1
.sumber
/dev/urandom
kecuali Anda tahu bahwa Anda perlu/dev/random
;/dev/random
blok di Linux.od
perintah berbeda. Keduanya hanya mencetak 4-byte integer yang tidak ditandai: 1st - from openssl, 2nd - from/dev/random
./dev/urandom
sebagai ganti/dev/random
- Saya tidak melihat alasan untuk menggunakannya/dev/random
, dan itu bisa sangat mahal / lambat, atau memperlambat bagian lain dari sistem. (Jangan ragu untuk mengedit kembali dan menjelaskan jika itu benar-benar diperlukan.)I
singkatan darisizeof(int)
itu mungkin kurang dari4
pada prinsipnya. btw,od -DAn
gagal(2**32-1)
tetapiod -N4 -tu4 -An
terus bekerja.Terima kasih atas semua jawaban Anda. Saya berakhir dengan solusi berikut, yang ingin saya bagikan.
Sebelum saya membahas lebih detail tentang mengapa dan bagaimana, inilah tl; dr : skrip baru saya yang mengkilap :-)
Simpan itu
~/bin/rand
dan Anda memiliki fungsi acak manis di bash yang dapat mencicipi integer dalam rentang sewenang-wenang yang diberikan. Rentang ini dapat berisi bilangan bulat negatif dan positif dan panjangnya dapat mencapai 2 60 -1:Semua ide dari penjawab lain sangat bagus. Jawaban oleh terdon , JF Sebastian , dan jimmij menggunakan alat eksternal untuk melakukan tugas dengan cara yang sederhana dan efisien. Namun, saya lebih suka solusi bash sejati untuk portabilitas maksimum, dan mungkin sedikit, hanya karena cinta untuk bash;)
Jawaban Ramesh dan l0b0 digunakan
/dev/urandom
atau/dev/random
dikombinasikan denganod
. Itu bagus, bagaimanapun, pendekatan mereka memiliki kelemahan hanya mampu sampel bilangan bulat acak dalam kisaran 0 hingga 2 8n -1 untuk beberapa n, karena metode ini sampel byte, yaitu, bitstrings dengan panjang 8. Ini adalah lompatan yang cukup besar dengan meningkat n.Akhirnya, jawaban Falco menggambarkan gagasan umum bagaimana ini bisa dilakukan untuk rentang arbitrer (tidak hanya kekuatan dua). Pada dasarnya, untuk rentang yang diberikan
{0..max}
, kita dapat menentukan apa kekuatan dua berikutnya, yaitu, persis berapa banyak bit yang diperlukan untuk mewakilimax
sebagai bitstring. Kemudian kita bisa mencicipi banyak bit itu dan melihat apakah bistring ini, sebagai bilangan bulat, lebih besar darimax
. Jika demikian, ulangi. Karena kami sampel bit yang diperlukan untuk mewakilimax
, setiap iterasi memiliki probabilitas lebih besar atau sama dengan 50% dari berhasil (50% dalam kasus terburuk, 100% dalam kasus terbaik). Jadi ini sangat efisien.Skrip saya pada dasarnya adalah implementasi konkret jawaban Falco, ditulis dalam bash murni dan sangat efisien karena menggunakan operasi bitwise bawaan bash untuk mengambil sampel bitstring dengan panjang yang diinginkan. Ini juga menghormati ide oleh Eliah Kagan yang menyarankan untuk menggunakan
$RANDOM
variabel bawaan dengan meringkas bitstring yang dihasilkan dari pemanggilan berulang$RANDOM
. Saya benar-benar mengimplementasikan kedua kemungkinan untuk menggunakan/dev/urandom
dan$RANDOM
. Secara default, skrip di atas menggunakan$RANDOM
. (Dan ok, jika menggunakan/dev/urandom
kita perlu od dan tr , tetapi ini didukung oleh POSIX.)Jadi bagaimana cara kerjanya?
Sebelum saya membahas hal ini, dua pengamatan:
Ternyata bash tidak dapat menangani bilangan bulat yang lebih besar dari 2 63 -1. Lihat diri mu sendiri:
Tampaknya bash secara internal menggunakan integer 64-bit yang ditandatangani untuk menyimpan integer. Jadi, pada 2 63 itu "membungkus" dan kami mendapatkan bilangan bulat negatif. Jadi kita tidak bisa berharap untuk mendapatkan rentang yang lebih besar dari 2 63 -1 dengan fungsi acak apa pun yang kita gunakan. Bash tidak bisa mengatasinya.
Kapan pun kita ingin mengambil sampel dalam rentang yang sewenang-wenang antara
min
danmax
dengan yang mungkinmin != 0
, kita bisa dengan mudah mengambil sampel di antara0
danmax-min
alih-alih kemudian menambahkanmin
ke hasil akhir. Ini bekerja bahkan jikamin
dan mungkin jugamax
yang negatif , tapi kami harus berhati-hati untuk sampel nilai antara0
dan nilai absolut darimax-min
. Jadi, kita bisa fokus pada bagaimana sampel nilai acak antara0
dan bilangan bulat positif arbitrermax
. Sisanya mudah.Langkah 1: Tentukan berapa banyak bit yang diperlukan untuk mewakili integer (logaritma)
Jadi untuk nilai yang diberikan
max
, kami ingin tahu berapa banyak bit yang diperlukan untuk menyatakannya sebagai bitstring. Ini agar nantinya kita dapat secara acak sampel hanya sebanyak bit yang diperlukan, yang membuat skrip jadi efisien.Ayo lihat. Karena dengan
n
bit, kita dapat mewakili hingga nilai 2 n -1, maka jumlahn
bit yang diperlukan untuk mewakili nilai arbitrerx
adalah plafon (log 2 (x + 1)). Jadi, kita membutuhkan fungsi untuk menghitung langit-langit logaritma ke basis 2. Ini agak jelas:Kita membutuhkan kondisinya
n>0
sehingga jika tumbuh terlalu besar, membungkus dan menjadi negatif, loop dijamin akan berakhir.Langkah 2: Cicipi bitstring acak yang panjangnya
n
Gagasan yang paling portabel adalah menggunakan
/dev/urandom
(atau bahkan/dev/random
jika ada alasan kuat) atau$RANDOM
variabel bawaan bash . Mari kita lihat bagaimana melakukannya$RANDOM
terlebih dahulu.Opsi A: Menggunakan
$RANDOM
Ini menggunakan ide yang disebutkan oleh Eliah Kagan. Pada dasarnya, karena
$RANDOM
sampel bilangan bulat 15-bit, kita dapat menggunakan$((RANDOM<<15|RANDOM))
sampel bilangan bulat 30-bit. Itu berarti, menggeser doa pertama$RANDOM
sebesar 15 bit ke kiri, dan menerapkan bitwise atau dengan doa kedua$RANDOM
, efektif meringkas dua bitstring sampel secara independen (atau setidaknya sama independennya dengan built-in$RANDOM
berjalan bash ).Kita dapat mengulanginya untuk mendapatkan integer 45-bit atau 60-bit. Setelah itu bash tidak bisa mengatasinya lagi, tetapi ini berarti kita dapat dengan mudah mencicipi nilai acak antara 0 dan 2 60 -1. Jadi, untuk mengambil sampel bilangan bulat n-bit, kami ulangi prosedur sampai bitstring acak kami, yang panjangnya tumbuh dalam langkah 15-bit, memiliki panjang lebih besar atau sama dengan n. Akhirnya, kita memotong bit yang terlalu banyak dengan menggeser bitwise ke kanan, dan kita berakhir dengan integer acak n-bit.
Opsi B: Menggunakan
/dev/urandom
Atau, kita bisa menggunakan
od
dan/dev/urandom
mengambil sampel integer n-bit.od
akan membaca byte, yaitu, bitstrings of length 8. Demikian pula seperti dalam metode sebelumnya, kami sampel begitu banyak byte sehingga jumlah setara bit sampel lebih besar atau sama dengan n, dan memotong bit yang terlalu banyak.Jumlah byte terendah yang diperlukan untuk mendapatkan setidaknya n bit adalah kelipatan terendah dari 8 yang lebih besar atau sama dengan n, yaitu lantai ((n + 7) / 8).
Ini hanya bekerja hingga bilangan bulat 56-bit. Mengambil sampel satu byte lagi akan memberi kita integer 64-bit, yaitu nilai hingga 2 64 -1, yang tidak dapat ditangani oleh bash.
Menyatukan potongan: Dapatkan bilangan bulat acak dalam rentang acak
Kita dapat mencicipi
n
bitstring-bit sekarang, tetapi kami ingin mengambil contoh bilangan bulat dalam kisaran dari0
hinggamax
, seragam secara acak , di manamax
mungkin arbitrer, tidak harus kekuatan dua. (Kami tidak dapat menggunakan modulo karena itu menciptakan bias.)Inti mengapa kami berusaha sangat keras untuk sampel bit sebanyak yang diperlukan untuk mewakili nilai
max
, adalah bahwa kita sekarang dapat dengan aman (dan efisien) menggunakan loop untuk berulang kali sampeln
bitstring-bit sampai kita sampel nilai yang lebih rendah atau sama denganmax
. Dalam kasus terburuk (max
adalah kekuatan dua), setiap iterasi berakhir dengan probabilitas 50%, dan dalam kasus terbaik (max
adalah kekuatan dua minus satu), iterasi pertama berakhir dengan pasti.Membungkus semuanya
Akhirnya, kami ingin mengambil sampel bilangan bulat antara
min
danmax
, di manamin
danmax
dapat arbitrer, bahkan negatif. Seperti yang disebutkan sebelumnya, ini sekarang sepele.Mari kita letakkan semuanya dalam skrip bash. Lakukan beberapa penguraian argumen ... Kami ingin dua argumen
min
danmax
, atau hanya satu argumenmax
, di manamin
defaultnya adalah0
.... dan, akhirnya, untuk sampel secara acak di nilai antara
min
danmax
, kami sampel bilangan bulat acak antara0
dan nilai absolut darimax-min
, dan menambahmin
hasil akhir. :-)Terinspirasi oleh ini , saya mungkin mencoba menggunakan dieharder untuk menguji dan membandingkan PRNG ini, dan memasukkan temuan saya di sini. :-)
sumber
sizeof(int) == 8
(64bit) karena--format=u
random.Random
kelas menggunakan 53bit? generator untuk mengembalikan nomor acak besar acak (banyak pemanggilan),random.SystemRandom
apakah menggunakanos.urandom()
yang sama yang dapat diimplementasikan menggunakan/dev/urandom
.--format=u8
maka saya hardcode anggapannyasizeof(int)==8
. Di sisi lain, jika digunakan--format=uL
tidak ada masalah: Saya tidak berpikir ada platform yang memiliki integer 64-bit tetapi masih mendefinisikan int panjang sebagai sesuatu yang lebih rendah. Jadi pada dasarnya saya berpendapat--format=uL
memungkinkan untuk lebih banyak fleksibilitas. Apa yang kamu pikirkan?long long
yang bisa 64bit sementara int = long = 32bit pada beberapa platform. Anda seharusnya tidak mengklaim rentang 0,.2 ** 60 jika Anda tidak dapat menjaminnya di semua platform. Di sisi lain bash mungkin tidak mendukung rentang ini sendiri pada platform semacam itu (saya tidak tahu, mungkin ini menggunakan maxint_t dan kemudian u8 lebih tepat jika Anda ingin menegaskan rentang tetap (od
tidak mendukung menentukan maksint jika rentang Anda adalah apa pun platform-dependent bash? kisaran adalah) .Jika rentang bash tergantung pada sizeof lama maka uL mungkin lebih tepat). Apakah Anda ingin rentang penuh yang didukung bash di semua OS atau rentang tetap?Bisakah itu zsh?
Anda mungkin ingin menggunakan seed juga
rand48(seed)
. Lihatman zshmodules
danman 3 erand48
untuk deskripsi terperinci jika tertarik.sumber
python
tersedia di Redhat, sistem berbasis Debian.sumber
Jika Anda menginginkan angka dari 0 hingga (2 ^ n) -1 di mana n mod 8 = 0 Anda cukup mendapatkan n / 8 byte dari
/dev/random
. Misalnya, untuk mendapatkan representasi desimal acak,int
Anda dapat:Jika Anda ingin mengambil hanya n bit, Anda dapat mengambil byte (n / 8) byte terlebih dahulu dan bergeser ke jumlah yang Anda inginkan. Misalnya jika Anda ingin 15 bit:
Jika Anda benar-benar yakin bahwa Anda tidak peduli dengan kualitas keacakan dan Anda ingin menjamin waktu berjalan minimal yang dapat Anda gunakan
/dev/urandom
sebagai gantinya/dev/random
. Pastikan Anda tahu apa yang Anda lakukan sebelum menggunakan/dev/urandom
!sumber
n
byte acak dari/dev/urandom
dan format menggunakanod
. Semangat seperti jawaban ini . Keduanya sama-sama baik :) Meskipun keduanya memiliki kelemahan memiliki rentang tetap 0 hingga 2 ^ (n * 8) -1 bit, di mana n adalah jumlah byte. Saya lebih suka metode untuk rentang sewenang - wenang , hingga 2 ^ 32-1, tetapi juga yang lebih rendah. Ini menciptakan kesulitan bias./dev/urandom
sebagai ganti/dev/random
- Saya tidak melihat alasan untuk menggunakannya/dev/random
, dan itu bisa sangat mahal / lambat, atau memperlambat bagian lain dari sistem. (Jangan ragu untuk mengedit kembali dan menjelaskan jika itu benar-benar diperlukan.)/dev/urandom
hasilnya jauh lebih buruk daripada/dev/random
urandom yang tidak dapat digunakan dalam kebanyakan kasus. Sekali/dev/urandom
diinisialisasi (di awal sistem); hasilnya sama baiknya dengan/dev/random
hampir semua aplikasi di Linux. Pada beberapa sistem, acak dan urandom sama.--format=u
harus diganti dengan--format=u4
karenasizeof(int)
mungkin kurang dari4
teori./dev/random
dan/dev/urandom
tidak memuaskan, dan bahwa "Linux harus menambahkan RNG aman yang menghalangi sampai telah mengumpulkan entropi benih yang memadai dan setelah itu berperilaku sepertiurandom
."Dengan asumsi Anda tidak keberatan menggunakan alat eksternal, ini harus memenuhi persyaratan Anda:
Ini menggunakan
rand
fungsi perl yang mengambil batas atas sebagai parameter. Anda dapat mengaturnya sesuai keinginan Anda. Seberapa dekat hal ini dengan keacakan yang sebenarnya dalam definisi matematika abstrak berada di luar cakupan situs ini, tetapi seharusnya tidak ada masalah kecuali Anda memerlukannya untuk enkripsi yang sangat sensitif atau sejenisnya. Mungkin bahkan di sana tetapi saya tidak akan berani berpendapat.sumber
1^32-1
tetapi Anda harus men-tweak untuk nomor yang lebih besarAnda harus mendapatkan yang terdekat (2 ^ X) -1 sama atau parutan dari maksimum yang Anda inginkan dan mendapatkan jumlah bit. Kemudian panggil / dev / acak beberapa kali dan tambahkan semua bit sampai Anda punya cukup, memotong semua bit yang terlalu banyak. Jika angka yang dihasilkan lebih besar dari max repeat Anda. Dalam kasus terburuk Anda memiliki peluang lebih besar dari 50% untuk mendapatkan nomor acak di bawah Maksimum Anda sehingga (untuk kasus terburuk ini), Anda akan menerima dua panggilan rata-rata.
sumber
/dev/urandom
, tetapi di kedua jawaban itu selalu kelipatan 8 bit. Memotong bit yang terlalu banyak untuk rentang yang lebih rendah sebelum memformat ke desimalod
adalah ide yang baik untuk meningkatkan efisiensi, karena loop hanya memiliki jumlah iterasi 2 yang diharapkan, seperti yang Anda jelaskan dengan baik. Ini, dikombinasikan dengan salah satu dari jawaban yang disebutkan, mungkin adalah cara untuk pergi.Jawaban Anda menarik tetapi cukup panjang.
Jika Anda ingin angka besar sewenang-wenang, maka Anda dapat bergabung dengan beberapa angka acak dalam sebuah bantuan:
Jika masalahnya bias, maka hapus saja.
Menggabungkan fungsi-fungsi ini bersama-sama
sumber