30

I would like to generate one or few random numbers separated by new line.

How this can be done?

2
  • 1
    Pure vim? Or, say, using :python?
    – muru
    Feb 14, 2015 at 22:28
  • Pure vim preferred, but if it's not build-in, any easy memorable method is fine.
    – kenorb
    Feb 14, 2015 at 22:36

8 Answers 8

25

Starting 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).


UNIX Shell (/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

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) }

Python

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.

Windows PowerShell

You can use Get-Random to get a random number:

:echo system('Get-Random')

Windows 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.

9
  • Instead of tr -d '[:space:]', perhaps tr -cd '[:digit:]' for the grep filter?
    – muru
    Feb 14, 2015 at 23:33
  • @muru I'm not sure that will work on OSX, which is why I didn't use it... You also get the numbers seperated by a newline this way, like the OP asked.... Feb 14, 2015 at 23:35
  • If that's the case.... GNU beats BSD. :P
    – muru
    Feb 14, 2015 at 23:36
  • One can also use the shell (system) for inputting random numbers, i.e. :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom, would generate 3 random signed integers.
    – jyalim
    Feb 15, 2015 at 7:13
  • 1
    @kenorb This will correctly use the shell for inputting 3 signed random integers: :r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
    – jyalim
    Feb 16, 2015 at 17:49
13

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:

  • A "randomish" initialization of the seeds;
  • A way to read some user-defined seed from a text file;
  • A pseudo random generator
  • Several random functions:
    • A function that generates a random number within a range
    • A dice rolling function
    • A function to randomly swap a list of sequential integers

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.

8

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
7

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]
4

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:

4
  • It also works with zsh, but not with sh, dash, fish, csh, or tcsh ... You could use :r! bash -c 'echo $RANDOM' ... Feb 15, 2015 at 23:24
  • "return last few digits from the current timestamp" -> This is not random. Using the time to get a pseudo-random number is almost always a bad idea, even for non-crypto purposes. What if the timestamp's resolution is in seconds and you create a "file.$random" twice in a second? Oops! ... Plus, you never know when someone's going to use your 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
  • I don't know what the quality of $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
  • 1
    I didn't know about $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".
    – Dalker
    Oct 7, 2016 at 14:46
3

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.

4
  • @Carpetsmoker 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).
    – muru
    Feb 14, 2015 at 23:47
  • @Carpetsmoker aargh, typo. The bracket after 60 is supposed to be after 10000. And xrange is not in Python3 because range is equivalent (neither actually constructs a list, unlike range in Py2).
    – muru
    Feb 14, 2015 at 23:55
  • @Carpetsmoker And the final difference is that map in Py3 returns an iterable and not a list, so the last line would use list(map(str, random.sample(range(10000), 60)))
    – muru
    Feb 14, 2015 at 23:58
  • Okay, I've taken the liberty to edit your post with some (minor) changes to make it compatible with both Python 2 and 3... Feb 15, 2015 at 0:51
2

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@:
0

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"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.