I would like to generate one or few random numbers separated by new line.
How this can be done?
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this communityStarting with Vim 8.1.2342, you can make use of the rand()
and srand()
functions.
Here is an example:
let g:seed = srand()
echo rand(g:seed) % 100 " to echo a random number between 0-99
For older Vims, you'll have to fall back to calling some external commands (or use the Python, Perl, Tcl, MzScheme, Ruby, Lua language interfaces, if your Vim comes with those enabled).
/bin/sh
)Calling:
strings -n 1 < /dev/urandom | tr -d '[:space:]' | head -c15
with system()
is a good way. You can get only numbers by replacing tr
with grep
:
strings -n 1 < /dev/urandom | grep -o '[[:digit:]]' | head -c15
You can use this in Vim like so:
:echo system("strings -n 1 < /dev/urandom | grep -o '[[:digit:]]' | head -c15")
The number 15 is the amount of numbers you want (adjust accordingly). This should work on Linux, BSD, OSX, and other UNIX systems. It won't work on MS Windows.
Also see my weblog post "Generate passwords from the commandline" (there are a lot of bad solutions for this out there).
Ruby is probably the next best choice, since Ruby scripting seems to be a bit more common than Python scripting. Getting a random number is easy:
:ruby puts Random.rand(10)
Or to get 15 numbers:
:ruby 15.times { puts Random.rand(10) }
You can use the random module; to get a single number:
:py import random; print(random.randint(0, 9))
Or a 15 numbers:
:py import random
:py for i in range(0, 15): print(random.randint(0, 9))
This should work for both Python 2 & 3.
You can use Get-Random
to get a random number:
:echo system('Get-Random')
cmd.exe
Windows 7 and later should ship with PowerShell, but if you want maximum
compatibility you can use cmd.exe
. It has a special variable %RANDOM%
:
:echo system('@echo %RANDOM%')
Note: This is not very random! , it uses the time (!)
Note that you don't need to use the Ruby or Python bindings to use the Ruby or
Python solutions; you could also create a separate script and call them with
system("python -c '...'")
(this does require that ruby/python is installed,
obviously.
tr -d '[:space:]'
, perhaps tr -cd '[:digit:]'
for the grep filter?
:r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom
, would generate 3 random signed integers.
:r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
Here is a pure vimscript solution. I did not create it, it was developed by Charles E. Campbell. You can find a Github repo with his code here.
The algorithm uses 3 seeds generated at Vim startup and generate a pseudo-random number based on calculations and permutations applied to the seeds:
" 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
The variables scope is declared as global because they are used by the generator function but it could be restricted to the script (s:
)
And here is the generator function:
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
The repo includes the following functions:
Here is a quick test I wrote to test the generator: I generated 1000000 numbers between 0 and 9 and counted the number of occurrences of each number here are the results:
0 : 100409
1 : 99435
2 : 100433
3 : 99578
4 : 100484
5 : 99948
6 : 100394
7 : 99649
8 : 99803
9 : 99867
As you can see the generation seems to be well distributed. I'm aware that this is largely not enough to test a random generator so I'll try to make some extra analysis if I have some free time.
This is a method found in the vim-randomtag plugin, which is based on reading ... current time microseconds, usable when you just want some number, you don't care much about randomness quality, or have security concerns etc.:
function! s:randnum(max) abort
return str2nr(matchstr(reltimestr(reltime()), '\v\.@<=\d+')[1:]) % a:max
endfunction
You can use the rand()
and srand()
functions, provided your Vim binary includes the patch 8.1.2342.
As an example, to generate 10 random numbers separated by newlines:
let seed = srand()
echo range(10)->map({-> rand(g:seed)})->join("\n")
To generate 10 random numbers all inferior to 100:
let seed = srand()
echo range(10)->map({-> rand(g:seed) % 100})->join("\n")
To generate 10 random alphabetical strings of 5 characters:
let seed = srand()
echo range(10)
\ ->map({-> range(5)
\ ->map({-> (97+rand(g:seed) % 26)->nr2char()})->join('')})
\ ->join("\n")
To generate 10 random words (taken from the dictionary file /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")
To generate 10 sequences of 5 random words:
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")
To generate a random UUID version 4:
" Based on this answer: https://stackoverflow.com/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], '')
To apply a random color scheme:
let seed = srand()
let colorschemes = getcompletion('', 'color')
let len = colorschemes->len()
exe 'colo '..colorschemes[rand(seed) % len]
Vim doesn't offer native random generator, however if you have vim compiled with Python, the following method will append a random digit at the end of your line:
:py import vim, random; vim.current.line += str(random.randint(0, 9))
Note: To check if your vim supports Python, try: :echo has('python')
(1 for yes).
You can also use shell which offers $RANDOM
variable (works with bash/ksh/zsh) which returns a pseudorandom (0-32767), in example:
:r! echo $RANDOM
or:
:put =system('echo $RANDOM')
or:
:r! od -An -td -N1 /dev/urandom
On Windows, you've to have Cygwin/MSYS/SUA installed, or use %RANDOM%
variable as Carpetsmoker suggested.
If you don't have access to shell and Python, as for workaround, you use last few digits from the current timestamp, in example:
:put =reltimestr(reltime())[-2:]
Note: If you're using it quite often, write a simple function which will return reltimestr(reltime())[-4:]
.
Note: Above methods returns only a pseudorandom integer which should not be used to generate an encryption key.
To add more random numbers please press @: to repeat the command again. Or prefix with number (like 10@:) to add much more of random numbers separated by new lines.
Related:
zsh
, but not with sh
, dash
, fish
, csh
, or tcsh
... You could use :r! bash -c 'echo $RANDOM'
...
Feb 15, 2015 at 23:24
GetRandom()
function where good prng does matter, so it's better to just get it right from the start if possible (and it's almost always possible here!)
Feb 16, 2015 at 10:06
$RANDOM
is, but if this is a poor PRNG then that's not a reason to use an even poorer PRNG :-) Instead, upgrade to a better PRNG! As far as I know, /dev/urandom
is available on all platforms where bash
is commonly available, so I don't see a reason to not use it.
Feb 16, 2015 at 10:59
$RANDOM
. Seems like a very nice little tool, and even though it may be a "poor's man RNG" (as @Carpetsmoker points out), it definitely fits @kenorb's (who asked the question) requirement of "easily memorable".
I don't think there's a native function for random numbers in Vimscript.
A way using :python
(use it in a function, perhaps, with 10000 and 60 replaced by parameters):
: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
See my answer to Making a box in vim via python for a quick intro on Python scripting in Vim.
Since vim.current.buffer
is a list of strings, we can assign a list of strings to it the way we would in Python. random.sample
is just the simplest way I can think of to get a list of random integers in Python.
random.sample
repuires only two arguments on Python 2, str
is the builtin function to convert things to strings. Let me look up the Py3 equivalents (str
would be the same, will have to check xrange
and random.sample
).
xrange
is not in Python3 because range
is equivalent (neither actually constructs a list, unlike range
in Py2).
map
in Py3 returns an iterable and not a list, so the last line would use list(map(str, random.sample(range(10000), 60)))
Vim v8.1.2342 add rand
and srand
function.
I prefer this way to generate 20 random numbers in range 0-100 in current buffer:
:put =rand()%100
20@:
If you'd like to use a source outside your computer, random dot org will do. The command line is a bit not-easily-remembered, so I'd put it in a shell script, but this will give you twenty four-digit decimal random numbers.
:r!curl -s "https://www.random.org/integers/?num=20&min=1000&max=9990&col=1&base=10&format=plain&rnd=new"
:python
?