Como gerar números aleatórios?

23

Gostaria de gerar um ou alguns números aleatórios separados por nova linha.

Como isso pode ser feito?

kenorb
fonte
1
Pure vim? Ou, digamos, usando :python?
Muni
O Pure vim é o preferido, mas se não for incorporado, qualquer método fácil e memorável é bom.
Kenorb

Respostas:

20

Não há um recurso interno para isso, então você precisará usar algo externo.

Shell UNIX ( /bin/sh)

A ligar:

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

com system()é um bom caminho. Você pode obter apenas números substituindo trpor grep:

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

Você pode usar isso no Vim assim:

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

O número 15 é a quantidade de números que você deseja (ajuste de acordo). Isso deve funcionar no Linux, BSD, OSX e outros sistemas UNIX. Não vai funcionar no MS Windows.

Veja também o meu blog " Gerar senhas a partir da linha de comando " (existem muitas soluções ruins para isso).

Rubi

Ruby é provavelmente a próxima melhor opção, pois os scripts Ruby parecem ser um pouco mais comuns que os scripts Python. Obter um número aleatório é fácil:

:ruby puts Random.rand(10)

Ou para obter 15 números:

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

Python

Você pode usar o módulo aleatório ; para obter um único número:

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

Ou 15 números:

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

Isso deve funcionar para o Python 2 e 3.

Windows PowerShell

Você pode usar Get-Random para obter um número aleatório:

:echo system('Get-Random')

janelas cmd.exe

O Windows 7 e posterior devem ser fornecidos com o PowerShell, mas se você quiser compatibilidade máxima, poderá usá-lo cmd.exe. Tem uma variável especial %RANDOM%:

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

Nota: Isso não é muito aleatório! , usa o tempo (!)


Observe que você não precisa usar as ligações Ruby ou Python para usar as soluções Ruby ou Python; você também pode criar um script separado e chamá-lo com system("python -c '...'")(isso requer que o ruby ​​/ python esteja instalado, obviamente.

Martin Tournoij
fonte
Em vez de tr -d '[:space:]', talvez tr -cd '[:digit:]'para o filtro grep?
Muni
@muru Não tenho certeza de que funcionará no OSX, e é por isso que não o usei ... Você também obtém os números separados por uma nova linha dessa maneira, como o OP pediu ...
Martin Tournoij
Se for esse o caso ... O GNU vence o BSD. : P
muru
Pode-se também usar o shell (sistema) para inserir números aleatórios, ou seja :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom, geraria 3 números inteiros assinados aleatoriamente.
HAL 9001
1
@kenorb Isso usará corretamente o shell para inserir 3 números inteiros aleatórios assinados::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Aqui está uma solução pura para vimscript . Eu não o criei, ele foi desenvolvido por Charles E. Campbell. Você pode encontrar um repositório do Github com seu código aqui .


O algoritmo usa 3 sementes geradas na inicialização do Vim e gera um número pseudo-aleatório com base nos cálculos e permutações aplicados às sementes:

" 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

O escopo das variáveis ​​é declarado como global porque é usado pela função do gerador, mas pode ser restrito ao script ( s:)

E aqui está a função do gerador:

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

O repo inclui as seguintes funções:

  • Uma inicialização "aleatória" das sementes;
  • Uma maneira de ler algumas sementes definidas pelo usuário em um arquivo de texto;
  • Um gerador pseudo-aleatório
  • Várias funções aleatórias:
    • Uma função que gera um número aleatório dentro de um intervalo
    • Uma função de rolagem de dados
    • Uma função para trocar aleatoriamente uma lista de números inteiros seqüenciais

Aqui está um teste rápido que escrevi para testar o gerador: gerei 1000000 números entre 0 e 9 e contei o número de ocorrências de cada número. Aqui estão os resultados:

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

Como você pode ver, a geração parece estar bem distribuída. Estou ciente de que isso não é suficiente para testar um gerador aleatório, então tentarei fazer uma análise extra se tiver algum tempo livre.

statox
fonte
7

Este é um método encontrado no plug - in vim-randomtag , baseado na leitura ... microssegundos para o horário atual, utilizável quando você deseja apenas um número, não se importa muito com a qualidade da aleatoriedade ou se preocupa com a segurança, etc .:

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

O Vim não oferece gerador aleatório nativo; no entanto, se você tiver o vim compilado com Python, o método a seguir anexará um dígito aleatório no final da sua linha:

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

Nota: Para verificar se o seu vim suporta Python, tente: :echo has('python')(1 para sim).

Você também pode usar o shell que oferece $RANDOMvariável (funciona com bash / ksh / zsh) que retorna um pseudo-aleatório (0-32767), por exemplo:

:r! echo $RANDOM

ou:

:put =system('echo $RANDOM')

ou:

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

No Windows, você deve ter o Cygwin / MSYS / SUA instalado ou usar a %RANDOM%variável conforme sugerido por Carpetsmoker .

Se você não tem acesso ao shell e ao Python, como solução alternativa, use os últimos dígitos do carimbo de data / hora atual, por exemplo:

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

Nota: Se você o estiver usando com bastante frequência, escreva uma função simples que sim return reltimestr(reltime())[-4:].

Nota: Os métodos acima retornam apenas um número inteiro pseudoaleatório que não deve ser usado para gerar uma chave de criptografia.


Para adicionar mais números aleatórios, pressione @:para repetir o comando novamente. Ou prefixe com number (like 10@:) para adicionar muito mais números aleatórios separados por novas linhas.


Palavras-chave:

kenorb
fonte
Ele também funciona com zsh, mas não com sh, dash, fish, csh, ou tcsh... Você pode usar :r! bash -c 'echo $RANDOM'...
Martin Tournoij
"retorna os últimos dígitos do timestamp atual" -> Isso não é aleatório. Usar o tempo para obter um número pseudo-aleatório é quase sempre uma má idéia, mesmo para fins não criptográficos. E se a resolução do registro de data e hora estiver em segundos e você criar um "arquivo. $ Random" duas vezes em um segundo? Opa! ... Além disso, você nunca sabe quando alguém usará sua GetRandom()função onde a boa impressão é importante, por isso é melhor acertar desde o início, se possível (e quase sempre é possível aqui!)
Martin Tournoij
Não sei qual é a qualidade $RANDOM, mas se esse é um PRNG ruim, não é uma razão para usar um PRNG ainda mais pobre :-) Em vez disso, atualize para um PRNG melhor! Tanto quanto eu sei, /dev/urandomestá disponível em todas as plataformas onde bashestá comumente disponível, então não vejo um motivo para não usá-lo.
Martin Tournoij 16/02/2015
1
Eu não sabia $RANDOM. Parece uma pequena ferramenta muito boa, e mesmo que seja um "RNG do pobre" (como aponta a @Carpetsmoker), definitivamente se encaixa no requisito de "facilmente memorável" do @ kenorb (que fez a pergunta).
21816 Dalker
4

Você pode usar as funções rand()e srand(), desde que o seu binário Vim inclua o patch 8.1.2342 .


Como exemplo, para gerar 10 números aleatórios separados por novas linhas:

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

Para gerar 10 números aleatórios, todos inferiores a 100:

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

Para gerar 10 seqüências alfabéticas aleatórias de 5 caracteres:

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

Para gerar 10 palavras aleatórias (extraídas do arquivo do dicionário /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 gerar 10 sequências de 5 palavras aleatórias:

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 gerar uma versão UUID aleatória 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 um esquema de cores aleatório:

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

Eu não acho que exista uma função nativa para números aleatórios no Vimscript.

Uma maneira de usar :python(use-o em uma função, talvez, com 10000 e 60 substituídos 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

Veja minha resposta para Criando uma caixa no vim via python para uma rápida introdução sobre scripts em Python no Vim.

Como vim.current.bufferé uma lista de cadeias de caracteres, podemos atribuir uma lista de cadeias de caracteres da mesma maneira que atribuiríamos no Python. random.sampleé apenas a maneira mais simples de obter uma lista de números inteiros aleatórios em Python.

muru
fonte
O @Carpetsmoker random.sampleexige apenas dois argumentos no Python 2, stré a função interna de converter coisas em strings. Deixe-me procurar os equivalentes Py3 ( strseria o mesmo, terá que verificar xrangee random.sample).
muru
@Carpetsmoker aargh, erro de digitação. O colchete depois de 60 deve ser depois de 10000. E xrangenão está no Python3 porque rangeé equivalente (nem na verdade constrói uma lista, ao contrário rangedo Py2).
Muni
@Carpetsmoker E a diferença final é que mapem PY3 retorna um iterável e não uma lista, para a última linha usarialist(map(str, random.sample(range(10000), 60)))
Muru
Ok, eu tomei a liberdade de editar o seu post com alguns (menor) muda para torná-lo compatível com o Python 2 e 3 ...
Martin Tournoij