¿Cómo generar números aleatorios?

23

Me gustaría generar uno o pocos números aleatorios separados por una nueva línea.

¿Cómo se puede hacer esto?

kenorb
fuente
1
Puro vim? O, digamos, usando :python?
muru
Se prefiere vim puro, pero si no está integrado, cualquier método fácil de recordar está bien.
kenorb

Respuestas:

20

No hay una función integrada para esto, por lo que deberá usar algo externo.

Shell de UNIX ( /bin/sh)

Vocación:

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

con system()es una buena manera. Solo puede obtener números reemplazándolos trcon grep:

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

Puedes usar esto en Vim así:

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

El número 15 es la cantidad de números que desea (ajuste en consecuencia). Esto debería funcionar en Linux, BSD, OSX y otros sistemas UNIX. No funcionará en MS Windows.

También vea mi publicación en el blog " Generar contraseñas desde la línea de comandos " (hay muchas soluciones malas para esto).

Rubí

Ruby es probablemente la siguiente mejor opción, ya que las secuencias de comandos de Ruby parecen ser un poco más comunes que las de Python. Obtener un número aleatorio es fácil:

:ruby puts Random.rand(10)

O para obtener 15 números:

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

Pitón

Puedes usar el módulo aleatorio ; para obtener un solo número:

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

O un 15 números:

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

Esto debería funcionar para Python 2 y 3.

Windows PowerShell

Puedes usar Get-Random para obtener un número aleatorio:

:echo system('Get-Random')

Ventanas cmd.exe

Windows 7 y versiones posteriores deben enviarse con PowerShell, pero si desea la máxima compatibilidad, puede usarlas cmd.exe. Tiene una variable especial %RANDOM%:

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

Nota: ¡ Esto no es muy aleatorio! , usa el tiempo (!)


Tenga en cuenta que no necesita usar los enlaces Ruby o Python para usar las soluciones Ruby o Python; también podría crear un script separado y llamarlos con system("python -c '...'")(esto requiere que ruby ​​/ python esté instalado, obviamente).

Martin Tournoij
fuente
En lugar de tr -d '[:space:]', tal vez tr -cd '[:digit:]'para el filtro grep?
muru
@muru No estoy seguro de que funcione en OSX, por eso no lo usé ... También obtienes los números separados por una nueva línea de esta manera, como preguntó el OP ...
Martin Tournoij
Si ese es el caso ... GNU supera a BSD. : P
muru
También se puede usar el shell (sistema) para ingresar números aleatorios, es decir :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom, generaría 3 enteros con signo aleatorio.
HAL 9001
1
@kenorb Esto usará correctamente el shell para ingresar 3 enteros aleatorios firmados::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Aquí hay una solución vimscript pura . No lo creé, fue desarrollado por Charles E. Campbell. Puede encontrar un repositorio de Github con su código aquí .


El algoritmo utiliza 3 semillas generadas en el inicio de Vim y genera un número pseudoaleatorio basado en cálculos y permutaciones aplicadas a las semillas:

" 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

El alcance de las variables se declara como global porque son utilizadas por la función del generador pero podrían estar restringidas al script ( s:)

Y aquí está la función del generador:

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

El repositorio incluye las siguientes funciones:

  • Una inicialización "aleatoria" de las semillas;
  • Una forma de leer algunas semillas definidas por el usuario de un archivo de texto;
  • Un generador pseudoaleatorio
  • Varias funciones aleatorias:
    • Una función que genera un número aleatorio dentro de un rango
    • Una función de tirar dados
    • Una función para intercambiar aleatoriamente una lista de enteros secuenciales

Aquí hay una prueba rápida que escribí para probar el generador: generé 1000000 números entre 0 y 9 y conté el número de ocurrencias de cada número, aquí están los resultados:

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

Como puede ver, la generación parece estar bien distribuida. Soy consciente de que esto no es suficiente para probar un generador aleatorio, así que intentaré hacer un análisis adicional si tengo algo de tiempo libre.

statox
fuente
7

Este es un método que se encuentra en el complemento vim-randomtag , que se basa en la lectura ... microsegundos de tiempo actual, utilizable cuando solo desea un número, no le importa mucho la calidad de la aleatoriedad o tiene problemas de seguridad, etc.

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

Vim no ofrece un generador aleatorio nativo, sin embargo, si ha compilado vim con Python, el siguiente método agregará un dígito aleatorio al final de su línea:

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

Nota: Para verificar si su vim es compatible con Python, intente: :echo has('python')(1 para sí).

También puedes usar shell que ofrece $RANDOM variables (funciona con bash / ksh / zsh) que devuelve un pseudoaleatorio (0-32767), por ejemplo:

:r! echo $RANDOM

o:

:put =system('echo $RANDOM')

o:

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

En Windows, debe tener instalado Cygwin / MSYS / SUA, o utilizar %RANDOM%variables como sugirió Carpetsmoker .

Si no tiene acceso a Shell y Python, como solución alternativa, use los últimos dígitos de la marca de tiempo actual, por ejemplo:

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

Nota: Si lo usa con bastante frecuencia, escriba una función simple que return reltimestr(reltime())[-4:] .

Nota: Los métodos anteriores devuelven solo un entero pseudoaleatorio que no debe usarse para generar una clave de cifrado.


Para agregar más números aleatorios, presione @:para repetir el comando nuevamente. O prefijo con número (como10@: ) para agregar muchos más números aleatorios separados por nuevas líneas.


Relacionado:

kenorb
fuente
También trabaja con zsh, pero no con sh, dash, fish, csh, o tcsh... Usted podría utilizar :r! bash -c 'echo $RANDOM'...
Martin Tournoij
"devolver los últimos dígitos de la marca de tiempo actual" -> Esto no es aleatorio. Usar el tiempo para obtener un número pseudoaleatorio es casi siempre una mala idea, incluso para fines no criptográficos. ¿Qué sucede si la resolución de la marca de tiempo es en segundos y crea un "archivo. $ Aleatorio" dos veces en un segundo? ¡Uy! ... Además, nunca se sabe cuando alguien va a utilizar su GetRandom()función en la que buena PRNG hace la materia, por lo que es mejor simplemente hacer las cosas bien desde el principio, si es posible (y es casi siempre es posible aquí!)
Martin Tournoij
No sé cuál es la calidad $RANDOM, pero si este es un PRNG pobre, entonces esa no es una razón para usar un PRNG aún más pobre :-) En su lugar, ¡actualice a un PRNG mejor! Hasta donde sé, /dev/urandomestá disponible en todas las plataformas donde bashestá comúnmente disponible, por lo que no veo una razón para no usarlo.
Martin Tournoij
1
No sabía acerca $RANDOM. Parece una pequeña herramienta muy agradable, y aunque puede ser un "RNG de hombre pobre" (como señala @Carpetsmoker), definitivamente se ajusta al requisito de "kenorb (que hizo la pregunta) de" fácilmente memorable ".
Dalker
4

Puede usar las funciones rand()y srand(), siempre que su binario Vim incluya el parche 8.1.2342 .


Como ejemplo, para generar 10 números aleatorios separados por nuevas líneas:

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

Para generar 10 números aleatorios todos inferiores a 100:

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

Para generar 10 cadenas alfabéticas aleatorias de 5 caracteres:

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

Para generar 10 palabras al azar (tomadas del archivo del diccionario /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")

Para generar 10 secuencias de 5 palabras al azar:

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

Para generar un UUID aleatorio versión 4:

" 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], '')

Para aplicar un esquema de color aleatorio:

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

No creo que haya una función nativa para números aleatorios en Vimscript.

Una forma de usar :python(usarlo en una función, tal vez, con 10000 y 60 reemplazados por parámetros):

: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

Vea mi respuesta a Hacer un cuadro en vim a través de python para una introducción rápida sobre las secuencias de comandos de Python en Vim.

Como vim.current.bufferes una lista de cadenas, podemos asignarle una lista de cadenas de la misma forma que lo haríamos en Python. random.samplees la forma más simple en que puedo pensar para obtener una lista de enteros aleatorios en Python.

muru
fuente
@Carpetsmoker random.sampletiene solo dos argumentos en Python 2, stres la función integrada para convertir cosas en cadenas. Permítanme buscar los equivalentes de Py3 ( strsería lo mismo, tendré que verificar xrangey random.sample).
muru
@Carpetsmoker aargh, error tipográfico. Se supone que el paréntesis después de 60 es después de 10000. Y xrangeno está en Python3 porque rangees equivalente (ninguno construye una lista, a diferencia rangede Py2).
muru
@Carpetsmoker Y la diferencia final es que mapen Py3 devuelve un iterable y no una lista, por lo que la última línea usaríalist(map(str, random.sample(range(10000), 60)))
muru
Bien, me he tomado la libertad de editar tu publicación con algunos cambios (menores) para que sea compatible con Python 2 y 3 ...
Martin Tournoij