Comment générer des nombres aléatoires?

23

Je voudrais générer un ou quelques nombres aléatoires séparés par une nouvelle ligne.

Comment cela peut-il se faire?

kenorb
la source
1
Pure vim? Ou, disons, en utilisant :python?
muru
Pure vim préféré, mais s'il n'est pas intégré, toute méthode facile à mémoriser convient.
kenorb

Réponses:

20

Il n'y a pas de fonction intégrée pour cela, vous devrez donc utiliser quelque chose d'extérieur.

Shell UNIX ( /bin/sh)

Appel:

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

avec system()est un bon moyen. Vous ne pouvez obtenir que des chiffres en les remplaçant trpar grep:

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

Vous pouvez utiliser ceci dans Vim comme ceci:

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

Le nombre 15 est le nombre de nombres que vous souhaitez (ajustez en conséquence). Cela devrait fonctionner sur Linux, BSD, OSX et d'autres systèmes UNIX. Cela ne fonctionnera pas sur MS Windows.

Voir également mon article de blog " Générer des mots de passe à partir de la ligne de commande " (il existe de nombreuses mauvaises solutions pour cela).

Rubis

Ruby est probablement le deuxième meilleur choix, car les scripts Ruby semblent être un peu plus courants que les scripts Python. Obtenir un nombre aléatoire est facile:

:ruby puts Random.rand(10)

Ou pour obtenir 15 numéros:

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

Python

Vous pouvez utiliser le module aléatoire ; pour obtenir un numéro unique:

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

Ou 15 numéros:

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

Cela devrait fonctionner pour Python 2 et 3.

Windows PowerShell

Vous pouvez utiliser Get-Random pour obtenir un nombre aléatoire:

:echo system('Get-Random')

les fenêtres cmd.exe

Windows 7 et versions ultérieures devraient être livrés avec PowerShell, mais si vous souhaitez une compatibilité maximale, vous pouvez utiliser cmd.exe. Il a une variable spéciale %RANDOM%:

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

Remarque: ce n'est pas très aléatoire! , il utilise le temps (!)


Notez que vous n'avez pas besoin d'utiliser les liaisons Ruby ou Python pour utiliser les solutions Ruby ou Python; vous pouvez également créer un script séparé et les appeler avec system("python -c '...'")(cela nécessite évidemment l'installation de ruby ​​/ python.

Martin Tournoij
la source
Au lieu de tr -d '[:space:]', peut-être tr -cd '[:digit:]'pour le filtre grep?
muru le
@muru Je ne suis pas sûr que cela fonctionnera sur OSX, c'est pourquoi je ne l'ai pas utilisé ... Vous obtenez également les numéros séparés par une nouvelle ligne de cette façon, comme l'OP l'a demandé ....
Martin Tournoij
Si c'est le cas .... GNU bat BSD. : P
muru
On peut également utiliser le shell (système) pour entrer des nombres aléatoires, c'est :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandom-à- dire générer 3 entiers signés aléatoires.
HAL 9001
1
@kenorb Cela utilisera correctement le shell pour entrer 3 entiers aléatoires signés::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Voici une solution Vimscript pure . Je ne l'ai pas créé, il a été développé par Charles E. Campbell. Vous pouvez trouver un dépôt Github avec son code ici .


L'algorithme utilise 3 graines générées au démarrage de Vim et génère un nombre pseudo-aléatoire basé sur des calculs et permutations appliqués aux graines:

" 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

La portée des variables est déclarée globale car elle est utilisée par la fonction générateur mais elle peut être limitée au script ( s:)

Et voici la fonction générateur:

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

Le dépôt comprend les fonctions suivantes:

  • Une initialisation "aléatoire" des graines;
  • Un moyen de lire une graine définie par l'utilisateur à partir d'un fichier texte;
  • Un générateur pseudo aléatoire
  • Plusieurs fonctions aléatoires:
    • Une fonction qui génère un nombre aléatoire dans une plage
    • Une fonction de lancer de dés
    • Une fonction pour échanger au hasard une liste d'entiers séquentiels

Voici un test rapide que j'ai écrit pour tester le générateur: j'ai généré 1000000 nombres entre 0 et 9 et compté le nombre d'occurrences de chaque nombre voici les résultats:

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

Comme vous pouvez le voir, la génération semble bien répartie. Je suis conscient que ce n'est largement pas suffisant pour tester un générateur aléatoire, je vais donc essayer de faire une analyse supplémentaire si j'ai du temps libre.

statox
la source
7

Il s'agit d'une méthode trouvée dans le plugin vim-randomtag , qui est basée sur la lecture ... microsecondes de l'heure actuelle, utilisable lorsque vous voulez juste un certain nombre, vous ne vous souciez pas beaucoup de la qualité de l'aléatoire, ou avez des problèmes de sécurité, etc.:

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

Vim n'offre pas de générateur aléatoire natif, cependant si vous avez compilé vim avec Python, la méthode suivante ajoutera un chiffre aléatoire à la fin de votre ligne:

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

Remarque: Pour vérifier si votre vim prend en charge Python, essayez: :echo has('python')(1 pour oui).

Vous pouvez également utiliser un shell qui offre une $RANDOMvariable (fonctionne avec bash / ksh / zsh) qui renvoie un pseudo-aléatoire (0-32767), par exemple:

:r! echo $RANDOM

ou:

:put =system('echo $RANDOM')

ou:

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

Sous Windows, vous devez avoir installé Cygwin / MSYS / SUA ou utiliser une %RANDOM%variable comme le suggère Carpetsmoker .

Si vous n'avez pas accès au shell et à Python, comme pour la solution de contournement, vous utilisez les derniers chiffres de l'horodatage actuel, par exemple:

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

Remarque: Si vous l'utilisez assez souvent, écrivez une fonction simple qui le fera return reltimestr(reltime())[-4:].

Remarque: les méthodes ci-dessus ne renvoient qu'un entier pseudo-aléatoire qui ne doit pas être utilisé pour générer une clé de chiffrement.


Pour ajouter d'autres nombres aléatoires, appuyez sur @:pour répéter la commande à nouveau. Ou préfixez avec un nombre (comme 10@:) pour ajouter beaucoup plus de nombres aléatoires séparés par de nouvelles lignes.


En relation:

kenorb
la source
Il fonctionne aussi avec zsh, mais pas avec sh, dash, fish, cshou tcsh... vous pouvez utiliser :r! bash -c 'echo $RANDOM'...
Martin Tournoij
"retourne les derniers chiffres de l'horodatage actuel" -> Ce n'est pas aléatoire. Utiliser le temps pour obtenir un nombre pseudo-aléatoire est presque toujours une mauvaise idée, même à des fins non cryptographiques. Et si la résolution de l'horodatage est en secondes et que vous créez un "fichier. $ Aléatoire" deux fois en une seconde? Oops! ... De plus, vous ne savez jamais quand quelqu'un va utiliser votre GetRandom()fonction là où un bon prng est important, il est donc préférable de le faire dès le début si possible (et c'est presque toujours possible ici!)
Martin Tournoij
Je ne sais pas quelle est la qualité de $RANDOM, mais si c'est un mauvais PRNG alors ce n'est pas une raison pour utiliser un PRNG encore plus pauvre :-) Au lieu de cela, passez à un meilleur PRNG! Pour autant que je sache, /dev/urandomest disponible sur toutes les plates-formes où il bashest couramment disponible, donc je ne vois pas de raison de ne pas l' utiliser.
Martin Tournoij
1
Je n'en savais rien $RANDOM. Cela semble être un très bon petit outil, et même s'il peut s'agir d'un "pauvre homme RNG" (comme le souligne @Carpetsmoker), il correspond certainement à l'exigence de "facilement mémorisable" de @ kenorb (qui a posé la question).
Dalker
4

Vous pouvez utiliser les fonctions rand()et srand(), à condition que votre binaire Vim inclue le patch 8.1.2342 .


Par exemple, pour générer 10 nombres aléatoires séparés par des retours à la ligne:

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

Pour générer 10 nombres aléatoires tous inférieurs à 100:

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

Pour générer 10 chaînes alphabétiques aléatoires de 5 caractères:

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

Pour générer 10 mots aléatoires (extraits du fichier dictionnaire /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")

Pour générer 10 séquences de 5 mots aléatoires:

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

Pour générer un UUID aléatoire version 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], '')

Pour appliquer un jeu de couleurs aléatoire:

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

Je ne pense pas qu'il existe une fonction native pour les nombres aléatoires dans Vimscript.

Une façon d'utiliser :python(utilisez-la dans une fonction, peut-être, avec 10000 et 60 remplacés par des paramètres):

: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

Voir ma réponse à Making a box in vim via python pour une introduction rapide sur les scripts Python dans Vim.

Puisqu'il vim.current.buffers'agit d'une liste de chaînes, nous pouvons lui affecter une liste de chaînes comme nous le ferions en Python. random.sampleest juste la façon la plus simple à laquelle je peux penser pour obtenir une liste d'entiers aléatoires en Python.

muru
la source
@Carpetsmoker random.samplene repousse que deux arguments sur Python 2, strc'est la fonction intégrée pour convertir les choses en chaînes. Permettez-moi de rechercher les équivalents Py3 ( strserait le même, devra vérifier xrangeet random.sample).
muru le
@Carpetsmoker aargh, faute de frappe. La parenthèse après 60 est supposée être après 10000. Et xrangen'est pas en Python3 car elle rangeest équivalente (ni ne construit réellement une liste, contrairement rangeà Py2).
muru
@Carpetsmoker Et la dernière différence est que mapdans Py3 renvoie un itérable et non une liste, donc la dernière ligne utiliseraitlist(map(str, random.sample(range(10000), 60)))
muru
D'accord, j'ai pris la liberté de modifier votre message avec quelques modifications (mineures) pour le rendre compatible avec Python 2 et 3 ...
Martin Tournoij