Jak wygenerować losowe liczby?

23

Chciałbym wygenerować jedną lub kilka liczb losowych oddzielonych nową linią.

Jak można to zrobić?

kenorb
źródło
1
Czysty vim? Lub powiedzmy za pomocą :python?
muru
Preferowany jest czysty vim, ale jeśli nie jest wbudowany, każda łatwa do zapamiętania metoda jest w porządku.
kenorb

Odpowiedzi:

20

Nie ma do tego wbudowanej funkcji, więc musisz użyć czegoś zewnętrznego.

Powłoka UNIX ( /bin/sh)

Powołanie:

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

z system()jest dobrym sposobem. Możesz uzyskać tylko liczby, zastępując trje grep:

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

Możesz użyć tego w Vimie tak:

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

Liczba 15 to liczba liczb, które chcesz (odpowiednio dostosuj). Powinno to działać w systemach Linux, BSD, OSX i innych systemach UNIX. To nie będzie działać na MS Windows.

Zobacz także mój post na blogu „ Generuj hasła z wiersza poleceń ” (istnieje wiele złych rozwiązań tego problemu).

Rubin

Ruby jest prawdopodobnie najlepszym wyborem, ponieważ skrypty Ruby wydają się być nieco bardziej popularne niż skrypty w języku Python. Uzyskanie losowej liczby jest łatwe:

:ruby puts Random.rand(10)

Lub zdobyć 15 numerów:

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

Pyton

Możesz użyć losowego modułu; aby uzyskać pojedynczy numer:

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

Lub 15 liczb:

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

Powinno to działać zarówno w Pythonie 2, jak i 3.

Windows PowerShell

Możesz użyć, Get-Random aby uzyskać losową liczbę:

:echo system('Get-Random')

Windows cmd.exe

Windows 7 i nowsze wersje powinny być dostarczane z programem PowerShell, ale jeśli chcesz uzyskać maksymalną kompatybilność, możesz użyć cmd.exe. Ma specjalną zmienną %RANDOM%:

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

Uwaga: To nie jest bardzo losowe! , wykorzystuje czas (!)


Zauważ, że nie musisz używać powiązań Ruby lub Python, aby korzystać z rozwiązań Ruby lub Python; możesz także utworzyć osobny skrypt i wywołać go za pomocą system("python -c '...'")(oczywiście wymaga to zainstalowania Ruby / Pythona.

Martin Tournoij
źródło
Zamiast tr -d '[:space:]', może tr -cd '[:digit:]'dla filtra grep?
muru
@muru Nie jestem pewien, czy będzie działał na OSX, dlatego go nie użyłem ... W ten sposób liczby są oddzielane przez nowy wiersz, tak jak poprosił OP ...
Martin Tournoij
Jeśli tak jest ... GNU bije BSD. : P
muru
Można również użyć powłoki (systemu) do wprowadzania liczb losowych, tj. :r! hexdump -n $((3*4)) -e '"%d"' /dev/urandomWygenerowałby 3 liczby całkowite ze znakiem losowym .
HAL 9001,
1
@kenorb Spowoduje to prawidłowe użycie powłoki do wprowadzenia 3 losowych liczb całkowitych ze znakiem::r! hexdump -n $((3*4)) -e '"\%d\n"' /dev/urandom
HAL 9001
12

Oto czyste rozwiązanie vimscript . Nie stworzyłem go, opracował go Charles E. Campbell. Można znaleźć repo GitHub z jego kod tutaj .


Algorytm wykorzystuje 3 nasiona wygenerowane przy starcie Vima i generuje pseudolosową liczbę na podstawie obliczeń i permutacji zastosowanych do nasion:

" 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

Zakres zmiennych jest zadeklarowany jako globalny, ponieważ są używane przez funkcję generatora, ale może być ograniczony do skryptu ( s:)

A oto funkcja generatora:

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

Repozytorium obejmuje następujące funkcje:

  • „Losowa” inicjalizacja nasion;
  • Sposób na odczyt niektórych nasion zdefiniowanych przez użytkownika z pliku tekstowego;
  • Pseudolosowy generator
  • Kilka losowych funkcji:
    • Funkcja generująca liczbę losową w zakresie
    • Funkcja rzutu kostką
    • Funkcja losowego zamiany listy kolejnych liczb całkowitych

Oto szybki test, który napisałem, aby przetestować generator: Wygenerowałem 1000000 liczb od 0 do 9 i policzyłem liczbę wystąpień każdej liczby tutaj są wyniki:

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

Jak widać, generacja wydaje się być dobrze rozproszona. Zdaję sobie sprawę, że w dużej mierze nie wystarcza to do przetestowania generatora losowego, więc spróbuję przeprowadzić dodatkową analizę, jeśli będę miał trochę wolnego czasu.

statox
źródło
7

Jest to metoda znaleźć w vim-randomtag wtyczki, która jest oparta na czytanie ... aktualne w czasie mikrosekund, użyteczny gdy chcesz po prostu jakiś numer, nie przejmował się przypadkowości jakości lub wątpliwości związanych z bezpieczeństwem itp .:

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

Vim nie oferuje natywnego generatora losowego, jednak jeśli kompilujesz vima z Pythonem, następująca metoda doda losową cyfrę na końcu twojej linii:

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

Uwaga: Aby sprawdzić, czy Twój vim obsługuje Python, spróbuj: :echo has('python')(1 na yes).

Możesz także użyć powłoki, która oferuje $RANDOMzmienną (działa z bash / ksh / zsh), która zwraca pseudolosowy (0-32767), na przykład:

:r! echo $RANDOM

lub:

:put =system('echo $RANDOM')

lub:

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

W systemie Windows musisz mieć zainstalowany Cygwin / MSYS / SUA lub użyć %RANDOM%zmiennej, jak sugeruje Carpetsmoker .

Jeśli nie masz dostępu do powłoki i Pythona, tak jak w celu obejścia tego problemu, użyj kilku ostatnich cyfr z bieżącego znacznika czasu, na przykład:

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

Uwaga: Jeśli używasz go dość często, napisz prostą funkcję, która będzie return reltimestr(reltime())[-4:].

Uwaga: Powyższe metody zwracają tylko pseudolosowe liczby całkowite, których nie należy używać do generowania klucza szyfrowania.


Aby dodać więcej liczb losowych, naciśnij, @:aby powtórzyć polecenie ponownie. Lub przedrostek z liczbą (podobną 10@:), aby dodać znacznie więcej liczb losowych oddzielonych nowymi wierszami.


Związane z:

kenorb
źródło
Współpracuje także z zsh, ale nie z sh, dash, fish, csh, lub tcsh... można użyć :r! bash -c 'echo $RANDOM'...
Marcina, Tournoij
„zwróć kilka ostatnich cyfr z bieżącego znacznika czasu” -> To nie jest przypadkowe. Wykorzystanie czasu na uzyskanie pseudolosowej liczby jest prawie zawsze złym pomysłem, nawet do celów innych niż szyfrowanie. Co jeśli rozdzielczość znacznika czasu jest wyrażona w sekundach, a „plik. $ Random” zostanie utworzony dwa razy na sekundę? Ups! ... Plus, nigdy nie wiesz, kiedy ktoś ma zamiar korzystać z GetRandom()funkcji gdzie dobry PRNG robi sprawa, więc lepiej po prostu zrobić to dobrze od samego początku, jeśli to możliwe (i to prawie zawsze możliwe tutaj!)
Martin Tournoij
Nie wiem, jaka jest jakość $RANDOM, ale jeśli jest to zły PRNG, to nie jest powód, aby używać jeszcze gorszego PRNG :-) Zamiast tego, uaktualnij do lepszego PRNG! O ile mi wiadomo, /dev/urandomjest dostępny na wszystkich platformach, na których bashjest powszechnie dostępny, więc nie widzę powodu, aby go nie używać.
Martin Tournoij
1
Nie wiedziałam o tym $RANDOM. Wydaje się, że jest to bardzo miłe, małe narzędzie i chociaż może to być „biedny człowiek RNG” (jak wskazuje @Carpetsmoker), zdecydowanie pasuje do wymagania @ kenorb (który zadał pytanie) „łatwej do zapamiętania”.
Dalker
4

Możesz używać funkcji rand()i srand(), pod warunkiem, że twój plik binarny Vim zawiera łatkę 8.1.2342 .


Na przykład, aby wygenerować 10 liczb losowych oddzielonych znakami nowej linii:

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

Aby wygenerować 10 liczb losowych o wartości mniejszej niż 100:

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

Aby wygenerować 10 losowych ciągów alfabetycznych po 5 znaków:

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

Aby wygenerować 10 losowych słów (wziętych z pliku słownika /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")

Aby wygenerować 10 sekwencji 5 losowych słów:

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

Aby wygenerować losowy UUID w wersji 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], '')

Aby zastosować losowy schemat kolorów:

let seed = srand()
let colorschemes = getcompletion('', 'color')
let len = colorschemes->len()
exe 'colo '..colorschemes[rand(seed) % len]
użytkownik938271
źródło
3

Nie sądzę, żeby w Vimscript była natywna funkcja dla liczb losowych.

Sposób użycia :python(użyj go w funkcji, być może z 10000 i 60 zastąpionymi parametrami):

: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

Zobacz moją odpowiedź na temat tworzenia skrzynki w vimie przez Pythona, aby uzyskać szybkie wprowadzenie do skryptów Pythona w Vimie.

Ponieważ vim.current.bufferjest to lista ciągów, możemy przypisać do niej listę ciągów tak, jak w Pythonie. random.sampleto po prostu najprostszy sposób na uzyskanie listy losowych liczb całkowitych w Pythonie.

muru
źródło
@Carpetsmoker random.samplenaprawia tylko dwa argumenty w Pythonie 2, strjest wbudowaną funkcją do konwersji rzeczy na ciągi. Pozwól, że sprawdzę ekwiwalenty Py3 ( strbyłoby to samo, będę musiał sprawdzić xrangei random.sample).
muru
@Carpetsmoker aargh, literówka. xrangeNawias po 60 powinien być po 10000. Nie ma go w Python3, ponieważ rangejest równoważny (w rzeczywistości żaden nie tworzy listy, w przeciwieństwie rangedo Py2).
muru
@Carpetsmoker I ostatnia różnica polega na tym, że mapw Py3 zwraca iterowalną, a nie listę, więc użyje ostatniej liniilist(map(str, random.sample(range(10000), 60)))
muru
Ok, pozwoliłem edytować swój post z kilkoma (drobnymi) zmianami, aby był kompatybilny zarówno z Pythonem 2, jak i 3 ...
Martin Tournoij