Come generare numeri casuali?


23

Vorrei generare uno o pochi numeri casuali separati da una nuova riga.

Come si può fare?


1
Vim puro? O, diciamo, usando :python?
muru,

Preferivo pure Vim, ma se non è integrato, qualsiasi metodo facile da ricordare va bene.
Kenorb,

Risposte:


20

Non esiste una funzione integrata per questo, quindi dovrai usare qualcosa di esterno.

Shell UNIX ( /bin/sh)

Calling:

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

con system()è un buon modo. Puoi ottenere solo numeri sostituendo trcon grep:

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

Puoi usarlo in Vim in questo modo:

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

Il numero 15 è la quantità di numeri che si desidera (regolare di conseguenza). Questo dovrebbe funzionare su Linux, BSD, OSX e altri sistemi UNIX. Non funzionerà su MS Windows.

Vedi anche il mio post sul blog " Genera password dalla riga di comando " (ci sono molte cattive soluzioni per questo là fuori).

Rubino

Ruby è probabilmente la scelta migliore successiva, dal momento che lo scripting Ruby sembra essere un po 'più comune dello scripting Python. Ottenere un numero casuale è facile:

:ruby puts Random.rand(10)

O per ottenere 15 numeri:

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

Pitone

Puoi usare il modulo casuale ; per ottenere un singolo numero:

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

O un 15 numeri:

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

Questo dovrebbe funzionare sia per Python 2 che per 3.

Windows PowerShell

Puoi usare Get-Random per ottenere un numero casuale:

:echo system('Get-Random')

finestre cmd.exe

Windows 7 e versioni successive dovrebbero essere forniti con PowerShell, ma se si desidera la massima compatibilità è possibile utilizzare cmd.exe. Ha una variabile speciale %RANDOM%:

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

Nota: questo non è molto casuale! , usa il tempo (!)


Si noti che non è necessario utilizzare i collegamenti Ruby o Python per utilizzare le soluzioni Ruby o Python; potresti anche creare uno script separato e chiamarli con system("python -c '...'")(questo richiede che ruby ​​/ python sia installato, ovviamente.


Invece di tr -d '[:space:]', forse tr -cd '[:digit:]'per il filtro grep?
muru,

@muru Non sono sicuro che funzionerà su OSX, motivo per cui non l'ho usato ... In questo modo ottieni anche i numeri separati da una nuova riga, come l'OP ha chiesto ....
Martin Tournoij

Se è così ... GNU batte BSD. : P
muru,

Si può anche usare la shell (sistema) per inserire numeri casuali, cioè :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandomgenerare 3 numeri casuali con segno.
HAL 9001,

1
@kenorb Questo utilizzerà correttamente la shell per l'immissione di 3 numeri interi casuali firmati::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001,

12

Ecco una pura soluzione vimscript . Non l'ho creato, è stato sviluppato da Charles E. Campbell. Puoi trovare un repository Github con il suo codice qui .


L'algoritmo utilizza 3 seed generati all'avvio di Vim e genera un numero pseudo-casuale basato su calcoli e permutazioni applicati ai seed:

" 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

L'ambito delle variabili è dichiarato globale perché vengono utilizzati dalla funzione generatore ma potrebbe essere limitato allo script ( s:)

Ed ecco la funzione del generatore:

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

Il repository include le seguenti funzioni:

  • Un'inizializzazione "casuale" dei semi;
  • Un modo per leggere alcuni seed definiti dall'utente da un file di testo;
  • Un generatore pseudo casuale
  • Diverse funzioni casuali:
    • Una funzione che genera un numero casuale all'interno di un intervallo
    • Una funzione di lancio dei dadi
    • Una funzione per scambiare casualmente un elenco di numeri interi sequenziali

Ecco un breve test che ho scritto per testare il generatore: ho generato 1000000 numeri tra 0 e 9 e ho contato il numero di occorrenze di ciascun numero qui sono i risultati:

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

Come puoi vedere, la generazione sembra essere ben distribuita. Sono consapevole che questo non è in gran parte sufficiente per testare un generatore casuale quindi proverò a fare qualche analisi extra se avrò del tempo libero.


7

Questo è un metodo che si trova nel plug -in vim-randomtag , che si basa sulla lettura ... microsecondi dell'ora corrente, utilizzabili quando si desidera solo un numero, non ci si preoccupa molto della qualità della casualità o si hanno problemi di sicurezza ecc .:

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

4

Vim non offre un generatore casuale nativo, tuttavia se hai compilato Vim con Python, il seguente metodo aggiungerà una cifra casuale alla fine della riga:

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

Nota: per verificare se Vim supporta Python, provare: :echo has('python')(1 per Sì).

Puoi anche usare la shell che offre $RANDOMvariabili (funziona con bash / ksh / zsh) che restituisce uno pseudocasuale (0-32767), ad esempio:

:r! echo $RANDOM

o:

:put =system('echo $RANDOM')

o:

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

Su Windows, devi avere Cygwin / MSYS / SUA installato o usare la %RANDOM%variabile come suggerito da Carpetsmoker .

Se non hai accesso a shell e Python, come per la soluzione alternativa, usi le ultime cifre del timestamp corrente, ad esempio:

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

Nota: se lo usi abbastanza spesso, scrivi una semplice funzione che lo farà return reltimestr(reltime())[-4:].

Nota: i metodi sopra riportati restituiscono solo un numero intero pseudocasuale che non deve essere utilizzato per generare una chiave di crittografia.


Per aggiungere più numeri casuali, premere @:per ripetere nuovamente il comando. Oppure 10@:aggiungi il prefisso con il numero (come ) per aggiungere molti più numeri casuali separati da nuove righe.


Relazionato:


Funziona anche con zsh, ma non con sh, dash, fish, csh, o tcsh... Si potrebbe usare :r! bash -c 'echo $RANDOM'...
Martin Tournoij

msgstr "restituisce le ultime cifre dal timestamp corrente" -> Questo non è casuale. Usare il tempo per ottenere un numero pseudo-casuale è quasi sempre una cattiva idea, anche per scopi non crittografici. Cosa succede se la risoluzione del timestamp è in secondi e si crea un "file. $ Random" due volte al secondo? Oops! ... Inoltre, non si sa mai quando qualcuno sta per usare la GetRandom()funzione di dove il buon PRNG fa importa, quindi è meglio per ottenere solo che fin dall'inizio, se possibile (ed è quasi sempre possibile qui!)
Martin Tournoij

Non so quale sia la qualità $RANDOM, ma se si tratta di un PRNG scadente, non è un motivo per utilizzare un PRNG ancora più povero :-) Invece, passare a un PRNG migliore! Per quanto ne so, /dev/urandomè disponibile su tutte le piattaforme in cui bashè comunemente disponibile, quindi non vedo un motivo per non usarlo.
Martin Tournoij,

1
Non lo sapevo $RANDOM. Sembra un piccolo strumento molto carino, e anche se può essere un "povero RNG" (come sottolinea @Carpetsmoker), si adatta sicuramente al requisito di @ kenorb (che ha posto la domanda) di "facilmente memorabile".
Dalker,

4

Puoi usare le funzioni rand()e srand(), a condizione che il tuo binario Vim includa la patch 8.1.2342 .


Ad esempio, per generare 10 numeri casuali separati da newline:

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

Per generare 10 numeri casuali tutti inferiori a 100:

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

Per generare 10 stringhe alfabetiche casuali di 5 caratteri:

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

Per generare 10 parole casuali (prese dal file del dizionario /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")

Per generare 10 sequenze di 5 parole casuali:

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

Per generare una versione UUID casuale 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], '')

Per applicare una combinazione di colori casuale:

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

3

Non credo che ci sia una funzione nativa per numeri casuali in Vimscript.

Un modo usando :python(usalo in una funzione, forse, con 10000 e 60 sostituiti da parametri):

: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

Vedi la mia risposta a Making a box in vim via python per una rapida introduzione sullo scripting Python in Vim.

Dato che vim.current.bufferè un elenco di stringhe, possiamo assegnargli un elenco come faremmo in Python. random.sampleè solo il modo più semplice che mi viene in mente di ottenere un elenco di numeri interi casuali in Python.


@Carpetsmoker random.sampleriproduce solo due argomenti su Python 2, strè la funzione integrata per convertire le cose in stringhe. Vorrei cercare gli equivalenti Py3 ( strsarebbe lo stesso, dovrà controllare xrangee random.sample).
muru,

@Carpetsmoker aargh, errore di battitura. La parentesi dopo 60 dovrebbe essere dopo 10000. E xrangenon è in Python3 perché rangeè equivalente (né costruisce effettivamente un elenco, a differenza rangedi Py2).
muru,

@Carpetsmoker E la differenza finale è che mapin Py3 restituisce un iterabile e non un elenco, quindi l'ultima riga userebbelist(map(str, random.sample(range(10000), 60)))
muru

Ok, mi sono preso la libertà di modificare il tuo post con alcune (minori) modifiche per renderlo compatibile con Python 2 e 3 ...
Martin Tournoij
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.