Wie werden Zufallszahlen generiert?

23

Ich möchte eine oder mehrere durch eine neue Zeile getrennte Zufallszahlen erzeugen.

Wie kann das gemacht werden?

Kenorb
quelle
1
Reines vim? Oder sagen wir mit :python?
muru
Reine vim bevorzugt, aber wenn es nicht eingebaut ist, ist jede einfache denkwürdige Methode in Ordnung.
Kenorb

Antworten:

20

Dafür gibt es keine eingebaute Einrichtung, Sie müssen also etwas Externes verwenden.

UNIX-Shell ( /bin/sh)

Berufung:

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

mit system()ist ein guter weg. Sie können nur Zahlen erhalten, indem Sie ersetzen trdurch grep:

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

Sie können dies in Vim folgendermaßen verwenden:

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

Die Zahl 15 ist die Anzahl der gewünschten Zahlen (entsprechend anpassen). Dies sollte unter Linux, BSD, OSX und anderen UNIX-Systemen funktionieren. Es funktioniert nicht unter MS Windows.

Siehe auch meinen Weblog-Beitrag " Passwörter über die Kommandozeile generieren " (es gibt viele schlechte Lösungen dafür).

Rubin

Ruby ist wahrscheinlich die nächstbeste Wahl, da Ruby-Skripte etwas häufiger zu sein scheinen als Python-Skripte. Eine Zufallszahl zu bekommen ist einfach:

:ruby puts Random.rand(10)

Oder um 15 Nummern zu bekommen:

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

Python

Sie können das Zufallsmodul verwenden . um eine einzelne Nummer zu bekommen:

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

Oder 15 Zahlen:

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

Dies sollte sowohl für Python 2 als auch für Python 3 funktionieren.

Windows PowerShell

Sie können verwenden Get-Random , um eine Zufallszahl zu erhalten:

:echo system('Get-Random')

Windows cmd.exe

Windows 7 und höher sollte mit PowerShell geliefert werden. Wenn Sie jedoch maximale Kompatibilität wünschen, können Sie dies verwenden cmd.exe. Es hat eine spezielle Variable %RANDOM%:

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

Hinweis: Dies ist nicht sehr zufällig! , es nutzt die Zeit (!)


Beachten Sie, dass Sie die Ruby- oder Python-Bindungen nicht verwenden müssen, um die Ruby- oder Python-Lösungen zu verwenden. Sie können auch ein separates Skript erstellen und es mit aufrufen system("python -c '...'")(dies setzt natürlich voraus, dass Ruby / Python installiert ist).

Martin Tournoij
quelle
Statt tr -d '[:space:]'vielleicht tr -cd '[:digit:]'für den grep filter?
muru
@muru Ich bin mir nicht sicher, ob das unter OSX funktionieren wird, weshalb ich es nicht verwendet habe ... Auf diese Weise werden die Nummern auch durch einen Zeilenumbruch getrennt, wie es das OP verlangt hat ....
Martin Tournoij
Wenn das der Fall ist ... schlägt GNU BSD. : P
muru
Man kann die Shell (System) auch zur Eingabe von Zufallszahlen verwenden, dh :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandomman würde 3 zufällige Ganzzahlen mit Vorzeichen erzeugen.
HAL 9001,
1
@kenorb Hiermit wird die Shell korrekt für die Eingabe von 3 vorzeichenbehafteten Zufallszahlen verwendet::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Hier ist eine reine Vimscript- Lösung. Ich habe es nicht erstellt, es wurde von Charles E. Campbell entwickelt. Ein Github-Repo mit seinem Code finden Sie hier .


Der Algorithmus verwendet 3 Seeds, die beim Start von Vim generiert werden, und generiert eine Pseudozufallszahl basierend auf Berechnungen und Permutationen, die auf die Seeds angewendet werden:

" 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

Der Gültigkeitsbereich der Variablen wird als global deklariert, da sie von der Generatorfunktion verwendet werden, aber möglicherweise auf script ( s:) beschränkt sind.

Und hier ist die Generatorfunktion:

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

Das Repo beinhaltet folgende Funktionen:

  • Eine "zufällige" Initialisierung der Seeds;
  • Eine Möglichkeit, einen benutzerdefinierten Startwert aus einer Textdatei zu lesen.
  • Ein Pseudozufallsgenerator
  • Mehrere zufällige Funktionen:
    • Eine Funktion, die eine Zufallszahl innerhalb eines Bereichs generiert
    • Eine Würfelfunktion
    • Eine Funktion zum zufälligen Vertauschen einer Liste aufeinanderfolgender Ganzzahlen

Hier ist ein kurzer Test, den ich geschrieben habe, um den Generator zu testen: Ich habe 1000000 Zahlen zwischen 0 und 9 generiert und die Anzahl der Vorkommen jeder Zahl gezählt. Hier sind die Ergebnisse:

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

Wie Sie sehen, scheint die Generation gut verteilt zu sein. Mir ist bewusst, dass dies zum Testen eines Zufallsgenerators im Großen und Ganzen nicht ausreicht. Daher werde ich versuchen, eine zusätzliche Analyse durchzuführen, wenn ich etwas Zeit habe.

statox
quelle
7

Dies ist eine Methode, die im vim-randomtag- Plugin gefunden wird und auf dem Lesen von ... Mikrosekunden in der aktuellen Zeit basiert. Sie kann verwendet werden, wenn Sie nur eine bestimmte Zahl möchten, wenn Sie nicht viel Wert auf die Qualität der Zufälligkeit legen oder Sicherheitsbedenken haben.

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

Vim bietet keinen nativen Zufallsgenerator. Wenn Sie Vim jedoch mit Python kompiliert haben, hängt die folgende Methode eine zufällige Ziffer am Ende Ihrer Zeile an:

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

Hinweis: Um zu überprüfen, ob Ihr Vim Python unterstützt, versuchen Sie Folgendes: :echo has('python')(1 für yes).

Sie können auch eine Shell verwenden, die eine $RANDOMVariable anbietet (funktioniert mit bash / ksh / zsh), die ein Pseudozufall (0-32767) zurückgibt. Beispiel:

:r! echo $RANDOM

oder:

:put =system('echo $RANDOM')

oder:

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

Unter Windows müssen Sie Cygwin / MSYS / SUA installiert haben oder eine %RANDOM%Variable verwenden, wie von Carpetsmoker vorgeschlagen.

Wenn Sie keinen Zugriff auf Shell und Python haben, wie zur Umgehung, verwenden Sie die letzten Ziffern des aktuellen Zeitstempels. Beispiel:

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

Hinweis: Wenn Sie es ziemlich oft verwenden, schreiben Sie eine einfache Funktion, die dies ermöglicht return reltimestr(reltime())[-4:].

Hinweis: Die obigen Methoden geben nur eine Pseudozufallszahl zurück, die nicht zum Generieren eines Verschlüsselungsschlüssels verwendet werden sollte.


Um weitere Zufallszahlen hinzuzufügen, drücken Sie @:, um den Befehl erneut zu wiederholen. Oder ein Präfix mit einer Zahl (wie 10@:), um viel mehr durch neue Zeilen getrennte Zufallszahlen hinzuzufügen.


Verbunden:

Kenorb
quelle
Es funktioniert auch mit zsh, aber nicht mit sh, dash, fish, csh, oder tcsh... könnten Sie :r! bash -c 'echo $RANDOM'...
Martin Tournoij
msgstr "Letzten Stellen des aktuellen Zeitstempels zurückgeben" -> Dies ist nicht zufällig. Die Zeit zu nutzen, um eine Pseudozufallszahl zu erhalten, ist fast immer eine schlechte Idee, auch für nicht kryptografische Zwecke. Was ist, wenn die Auflösung des Zeitstempels in Sekunden ist und Sie zweimal in einer Sekunde eine "Datei. $ Random" erstellen? Hoppla! ... Außerdem wissen Sie nie, wann jemand Ihre GetRandom()Funktion nutzen wird, wenn es auf gutes PRNG ankommt. Daher ist es besser, es möglichst von Anfang an richtig zu machen (und das ist hier fast immer möglich!)
Martin Tournoij,
Ich weiß nicht, was die Qualität von $RANDOMist, aber wenn dies ein schlechtes PRNG ist, dann ist das kein Grund, ein noch schlechteres PRNG zu verwenden :-) Aktualisieren Sie stattdessen auf ein besseres PRNG! Soweit ich weiß, /dev/urandomist es auf allen Plattformen verfügbar, auf denen bashes allgemein verfügbar ist. Daher sehe ich keinen Grund, es nicht zu verwenden.
Martin Tournoij
1
Ich wusste es nicht $RANDOM. Scheint wie ein sehr nettes kleines Werkzeug, und obwohl es ein "RNG des armen Mannes" sein mag (wie @Carpetsmoker betont), passt es definitiv zu der Anforderung von @kenorb (der die Frage gestellt hat) "leicht zu merken".
Dalker
3

Ich glaube nicht, dass es in Vimscript eine native Funktion für Zufallszahlen gibt.

Eine Möglichkeit zur Verwendung :python(verwenden Sie sie in einer Funktion, bei der 10000 und 60 durch Parameter ersetzt werden):

: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

In meiner Antwort auf " Eine Box in Vim über Python erstellen" finden Sie eine kurze Einführung in die Python-Skripterstellung in Vim.

Da vim.current.bufferes sich um eine Liste von Zeichenfolgen handelt, können wir ihr wie in Python eine Liste von Zeichenfolgen zuweisen. random.sampleDies ist der einfachste Weg, wie ich mir vorstellen kann, in Python eine Liste zufälliger Ganzzahlen zu erstellen.

muru
quelle
@Carpetsmoker random.samplebenötigt in Python 2 nur zwei Argumente, strist die eingebaute Funktion, um Dinge in Strings umzuwandeln. Lassen Sie mich die Py3-Entsprechungen nachschlagen ( strwäre gleich, müsste prüfen xrangeund random.sample).
muru
@ Carpetsmoker aargh, Tippfehler. Die Klammer nach 60 sollte nach 10000 sein. Und xrangeist nicht in Python3, weil sie rangeäquivalent ist (und im Gegensatz rangezu Py2 keine Liste erstellt).
muru
@Carpetsmoker Und der letzte Unterschied ist , dass mapin PY3 eine iterable zurückgibt und keine Liste, so dass die letzte Zeile verwenden würdelist(map(str, random.sample(range(10000), 60)))
muru
Okay, ich habe mir
erlaubt
3

Sie können die Funktionen rand()und srand()verwenden, sofern Ihre Vim-Binärdatei den Patch 8.1.2342 enthält .


Um beispielsweise 10 durch Zeilenumbrüche getrennte Zufallszahlen zu generieren, gehen Sie wie folgt vor:

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

So generieren Sie 10 Zufallszahlen, die kleiner als 100 sind:

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

So generieren Sie 10 zufällige alphabetische Zeichenfolgen mit 5 Zeichen:

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

So generieren Sie 10 zufällige Wörter (aus der Wörterbuchdatei /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")

So generieren Sie 10 Folgen von 5 zufälligen Wörtern:

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

So generieren Sie eine zufällige UUID 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], '')

So wenden Sie ein zufälliges Farbschema an:

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