Bagaimana cara menghasilkan angka acak?

23

Saya ingin menghasilkan satu atau beberapa angka acak yang dipisahkan oleh baris baru.

Bagaimana ini bisa dilakukan?

kenorb
sumber
1
Vim murni? Atau, katakanlah, menggunakan :python?
muru
Pure vim lebih disukai, tetapi jika tidak built-in, metode mudah yang mudah diingat tidak masalah.
kenorb

Jawaban:

20

Tidak ada fasilitas bawaan untuk ini, jadi Anda harus menggunakan sesuatu yang eksternal.

UNIX Shell ( /bin/sh)

Panggilan:

strings -n 1 < /dev/urandom | tr -d '[:space:]' | head -c15

dengan system()adalah cara yang baik. Anda hanya bisa mendapatkan nomor dengan mengganti trdengan grep:

strings -n 1 < /dev/urandom | grep -o '[[:digit:]]' | head -c15

Anda dapat menggunakan ini di Vim seperti:

:echo system("strings -n 1 < /dev/urandom | grep -o '[[:digit:]]'  | head -c15")

Angka 15 adalah jumlah angka yang Anda inginkan (sesuaikan sesuai). Ini harus bekerja pada Linux, BSD, OSX, dan sistem UNIX lainnya. Itu tidak akan berfungsi pada MS Windows.

Juga lihat posting weblog saya " Hasilkan kata sandi dari commandline " (ada banyak solusi buruk untuk ini di luar sana).

Rubi

Ruby mungkin adalah pilihan terbaik berikutnya, karena scripting Ruby tampaknya sedikit lebih umum daripada scripting Python. Mendapatkan nomor acak itu mudah:

:ruby puts Random.rand(10)

Atau untuk mendapatkan 15 nomor:

:ruby 15.times { puts Random.rand(10) }

Python

Anda dapat menggunakan modul acak ; untuk mendapatkan satu nomor:

:py import random; print(random.randint(0, 9))

Atau 15 angka:

:py import random
:py for i in range(0, 15): print(random.randint(0, 9))

Ini seharusnya bekerja untuk Python 2 & 3.

Windows PowerShell

Anda dapat menggunakan Get-Random untuk mendapatkan nomor acak:

:echo system('Get-Random')

Windows cmd.exe

Windows 7 dan yang lebih baru harus dikirim dengan PowerShell, tetapi jika Anda ingin kompatibilitas maksimum Anda dapat menggunakan cmd.exe. Ini memiliki variabel khusus %RANDOM%:

:echo system('@echo %RANDOM%')

Catatan: Ini tidak terlalu acak! , ini menggunakan waktu (!)


Perhatikan bahwa Anda tidak perlu menggunakan ikatan Ruby atau Python untuk menggunakan solusi Ruby atau Python; Anda juga dapat membuat skrip terpisah dan memanggil mereka dengan system("python -c '...'")(ini memang mengharuskan ruby ​​/ python diinstal, jelas.

Martin Tournoij
sumber
Alih-alih tr -d '[:space:]', mungkin tr -cd '[:digit:]'untuk filter grep?
muru
@muru Saya tidak yakin itu akan berfungsi pada OSX, itulah sebabnya saya tidak menggunakannya ... Anda juga mendapatkan angka yang dipisahkan oleh baris baru dengan cara ini, seperti yang diminta OP ....
Martin Tournoij
Jika itu masalahnya .... GNU mengalahkan BSD. : P
muru
Anda juga dapat menggunakan shell (sistem) untuk memasukkan angka acak, yaitu :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom, akan menghasilkan 3 bilangan bulat yang ditandatangani secara acak.
HAL 9001
1
@kenorb Ini dengan benar akan menggunakan shell untuk memasukkan 3 bilangan bulat acak yang ditandatangani::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Berikut ini adalah solusi vimscript murni . Saya tidak membuatnya, ini dikembangkan oleh Charles E. Campbell. Anda dapat menemukan repo Github dengan kode-nya di sini .


Algoritme menggunakan 3 biji yang dihasilkan pada startup Vim dan menghasilkan angka pseudo-acak berdasarkan perhitungan dan permutasi yang diterapkan pada biji:

" Randomization Variables
" with a little extra randomized start from localtime()
let g:rndm_m1 = 32007779 + (localtime()%100 - 50)
let g:rndm_m2 = 23717810 + (localtime()/86400)%100
let g:rndm_m3 = 52636370 + (localtime()/3600)%100

Ruang lingkup variabel dinyatakan sebagai global karena mereka digunakan oleh fungsi generator tetapi bisa dibatasi untuk skrip ( s:)

Dan di sini adalah fungsi generator:

function! Rndm()
    let m4= g:rndm_m1 + g:rndm_m2 + g:rndm_m3
    if( g:rndm_m2 < 50000000 )
        let m4= m4 + 1357
    endif
    if( m4 >= 100000000 )
        let m4= m4 - 100000000
        if( m4 >= 100000000 )
            let m4= m4 - 100000000
        endif
    endif
    let g:rndm_m1 = g:rndm_m2
    let g:rndm_m2 = g:rndm_m3
    let g:rndm_m3 = m4
    return g:rndm_m3
endfun

Repo mencakup fungsi-fungsi berikut:

  • Inisialisasi "acak" benih;
  • Cara membaca beberapa seed yang ditentukan pengguna dari file teks;
  • Generator acak semu
  • Beberapa fungsi acak:
    • Fungsi yang menghasilkan angka acak dalam rentang
    • Fungsi dadu bergulir
    • Fungsi untuk menukar daftar bilangan bulat berurutan secara acak

Berikut ini adalah tes cepat yang saya tulis untuk menguji generator: Saya menghasilkan 10.00000 angka antara 0 dan 9 dan menghitung jumlah kemunculan setiap angka di sini adalah hasilnya:

0 : 100409
1 : 99435
2 : 100433
3 : 99578
4 : 100484
5 : 99948
6 : 100394
7 : 99649
8 : 99803
9 : 99867

Seperti yang Anda lihat, generasi tampaknya terdistribusi dengan baik. Saya sadar bahwa ini sebagian besar tidak cukup untuk menguji generator acak jadi saya akan mencoba membuat beberapa analisis tambahan jika saya punya waktu luang.

statox
sumber
7

Ini adalah metode yang ditemukan dalam plugin vim-randomtag , yang didasarkan pada pembacaan ... mikrodetik waktu saat ini, dapat digunakan ketika Anda hanya menginginkan beberapa nomor, Anda tidak terlalu peduli tentang kualitas keacakan, atau memiliki masalah keamanan dll .:

function! s:randnum(max) abort
  return str2nr(matchstr(reltimestr(reltime()), '\v\.@<=\d+')[1:]) % a:max
endfunction
VanLaser
sumber
4

Vim tidak menawarkan generator acak asli, namun jika Anda memiliki vim yang dikompilasi dengan Python, metode berikut ini akan menambahkan digit acak di akhir baris Anda:

:py import vim, random; vim.current.line += str(random.randint(0, 9))

Catatan: Untuk memeriksa apakah vim Anda mendukung Python, coba: :echo has('python')(1 untuk ya).

Anda juga dapat menggunakan shell yang menawarkan $RANDOMvariabel (berfungsi dengan bash / ksh / zsh) yang mengembalikan pseudorandom (0-32767), misalnya:

:r! echo $RANDOM

atau:

:put =system('echo $RANDOM')

atau:

:r! od -An -td -N1 /dev/urandom

Di Windows, Anda harus menginstal Cygwin / MSYS / SUA, atau menggunakan %RANDOM%variabel seperti yang disarankan Carpetsmoker .

Jika Anda tidak memiliki akses ke shell dan Python, seperti untuk penyelesaiannya, Anda menggunakan beberapa digit terakhir dari cap waktu saat ini, misalnya:

:put =reltimestr(reltime())[-2:]

Catatan: Jika Anda cukup sering menggunakannya, tulislah fungsi sederhana yang akan return reltimestr(reltime())[-4:].

Catatan: Metode di atas hanya mengembalikan integer pseudorandom yang tidak boleh digunakan untuk menghasilkan kunci enkripsi.


Untuk menambahkan lebih banyak angka acak, silakan tekan @:untuk mengulangi perintah lagi. Atau awalan dengan angka (seperti 10@:) untuk menambahkan lebih banyak angka acak yang dipisahkan oleh baris baru.


Terkait:

kenorb
sumber
Ia juga bekerja dengan zsh, tetapi tidak dengan sh, dash, fish, csh, atau tcsh... Anda bisa menggunakan :r! bash -c 'echo $RANDOM'...
Martin Tournoij
"kembalikan beberapa digit terakhir dari cap waktu saat ini" -> Ini bukan acak. Menggunakan waktu untuk mendapatkan nomor pseudo-acak hampir selalu merupakan ide yang buruk, bahkan untuk tujuan non-crypto. Bagaimana jika resolusi stempel waktu dalam detik dan Anda membuat "file. $ Random" dua kali dalam satu detik? Ups! ... Plus, Anda tidak pernah tahu kapan seseorang akan menggunakan GetRandom()fungsi Anda di mana prng yang baik itu penting, jadi lebih baik untuk melakukannya dengan benar dari awal jika memungkinkan (dan hampir selalu memungkinkan di sini!)
Martin Tournoij
Saya tidak tahu apa kualitasnya $RANDOM, tetapi jika ini adalah PRNG yang buruk maka itu bukan alasan untuk menggunakan PRNG yang lebih buruk :-) Sebaliknya, tingkatkan ke PRNG yang lebih baik! Sejauh yang saya tahu, /dev/urandomtersedia di semua platform di mana bashbiasanya tersedia, jadi saya tidak melihat alasan untuk tidak menggunakannya.
Martin Tournoij
1
Saya tidak tahu $RANDOM. Sepertinya alat kecil yang sangat bagus, dan meskipun itu mungkin "orang miskin RNG" (seperti yang ditunjukkan oleh @Carpetsmoker), itu pasti cocok dengan persyaratan @ kenorb (yang mengajukan pertanyaan) persyaratan "mudah diingat".
Dalker
4

Anda dapat menggunakan rand()dan srand()fungsinya, asalkan biner Vim Anda menyertakan tambalan 8.1.2342 .


Sebagai contoh, untuk menghasilkan 10 angka acak yang dipisahkan oleh baris baru:

let seed = srand()
echo range(10)->map({-> rand(g:seed)})->join("\n")

Untuk menghasilkan 10 angka acak, semua lebih rendah dari 100:

let seed = srand()
echo range(10)->map({-> rand(g:seed) % 100})->join("\n")

Untuk menghasilkan 10 string alfabet acak dengan 5 karakter:

let seed = srand()
echo range(10)
   \ ->map({-> range(5)
   \           ->map({-> (97+rand(g:seed) % 26)->nr2char()})->join('')})
   \ ->join("\n")

Untuk menghasilkan 10 kata acak (diambil dari file kamus /usr/share/dict/words):

let seed = srand()
let words = readfile('/usr/share/dict/words')
let len = len(words)
echo range(10)->map({-> g:words[rand(g:seed) % g:len]})->join("\n")

Untuk menghasilkan 10 urutan 5 kata acak:

let seed = srand()
let words = readfile('/usr/share/dict/words')
let len = len(words)
echo range(10)
   \ ->map({-> range(5)
   \           ->map({-> g:words[rand(g:seed) % g:len]})->join()})
   \ ->join("\n")

Untuk menghasilkan UUID versi 4 acak:

" Based on this answer: /programming//a/38191078/9780968
let seed = srand()
let random_numbers = range(30)->map({-> rand(g:seed) % 16})
echo call('printf', ['%x%x%x%x%x%x%x%x-%x%x%x%x-4%x%x%x'] + random_numbers[:14])
 \ ..call('printf', ['-X%x%x%x-%x%x%x%x%x%x%x%x%x%x%x%x'] + random_numbers[15:])
 \   ->substitute('X', '89ab'[rand(seed) % 4], '')

Untuk menerapkan skema warna acak:

let seed = srand()
let colorschemes = getcompletion('', 'color')
let len = colorschemes->len()
exe 'colo '..colorschemes[rand(seed) % len]
pengguna938271
sumber
3

Saya tidak berpikir ada fungsi asli untuk angka acak di Vimscript.

Cara menggunakan :python(menggunakannya dalam suatu fungsi, mungkin, dengan 10000 dan 60 diganti dengan parameter):

:python <<EOF
import vim
import random

line = vim.current.window.cursor[0]
r = getattr(__builtins__, 'xrange', range) # To make it work for both Python 2 & 3
vim.current.buffer[line:line] = list(map(str, random.sample(r(10000), 60)))
EOF

Lihat jawaban saya untuk Membuat kotak dalam vim melalui python untuk intro cepat tentang skrip Python di Vim.

Karena vim.current.bufferini adalah daftar string, kita bisa menetapkan daftar string seperti yang kita lakukan dengan Python. random.samplehanyalah cara paling sederhana yang dapat saya pikirkan untuk mendapatkan daftar bilangan bulat acak dengan Python.

muru
sumber
@Carpetsmoker random.samplehanya mengulangi dua argumen pada Python 2, stradalah fungsi bawaan untuk mengubah berbagai hal menjadi string. Biarkan saya mencari yang setara dengan Py3 ( strakan sama, harus memeriksa xrangedan random.sample).
muru
@Carpetsmoker aargh, salah ketik. Tanda kurung setelah 60 seharusnya setelah 10.000. Dan xrangetidak dalam Python3 karena rangesama (tidak benar-benar membangun daftar, tidak seperti rangedalam Py2).
muru
@Carpetsmoker Dan perbedaan terakhir adalah bahwa mapdalam Py3 mengembalikan iterable dan bukan daftar, jadi baris terakhir akan menggunakanlist(map(str, random.sample(range(10000), 60)))
muru
Oke, saya telah mengambil kebebasan untuk mengedit posting Anda dengan beberapa perubahan (kecil) untuk membuatnya kompatibel dengan Python 2 dan 3 ...
Martin Tournoij