Generator haseł XKCD


34

Wprowadzenie

Najwyraźniej pytanie zostało tu zadane i niestety zostało zamknięte. Pomyślałem, że warto spróbować ponownie, ale zrobiłem to dobrze.

XKCD patrzy na to, jak jesteśmy szkoleni w używaniu „trudnych do zapamiętania haseł”, myśląc, że jest bezpieczny, ale zamiast tego złamanie komputera zajęłoby 3 dni. Z drugiej strony, zapamiętywanie 4-5 słów podnosi Intropy Kuan i jest łatwe do zapamiętania. Szalony, jak to działa, co?

Wyzwanie

Obecnie zadaniem jest utworzenie 5 haseł za pomocą słów. 4 słowa na hasło i minimum 4 litery na słowo, ale nie maksimum. Intropy hasła Kuan będą musiały zostać obliczone dla każdego hasła, ale wymuszone minimum nie zostanie ustawione.

Co to jest intropy hasła Kuan?

Według Kuan, hasło Intropy Kuana jest miarą nieprzewidywalności hasła. Jest proste obliczenie: E = log 2 (R), * L . E oznacza Intropy hasła Kuan, R oznacza zakres dostępnych znaków, a L długość hasła.

Zakres dostępnych znaków jest oczywisty. Jest to zakres znaków, które może zawierać hasło, w tym przypadku jest to duża i mała litera. Ponieważ w alfabecie jest 26 znaków, 26 x 2 = 52 znaków w całym zakresie hasła.

Długość hasła jest również oczywista. Jest to całkowita długość hasła po utworzeniu.

Ograniczenia

  • Brak wejścia.
  • Słowo nie może pojawić się ponownie z tym samym hasłem.
  • Hasło lub cyfry nie są dozwolone.
  • 4 słowa na hasło, ale wymuszone minimum 4 litery na słowo.
  • Brak spacji między słowami.
  • Nie można wygenerować tego samego hasła w kółko.
  • Każde słowo musi być pisane wielkimi literami w haśle.
  • Wyjście musi być czytelne dla człowieka, musi być rozstawione. Musi również zawierać hasło Kuan wraz z nim przy użyciu równania hasła Kuan powyżej.
  • Słownik . Musisz tego użyć, pobrać go jako plik tekstowy i odpowiednio zintegrować. To będzie lista, z której pobierasz słowa. Twój kod powinien zakładać, że jest dostępny.
  • To jest , najkrótsze bajty wygrywają.

Wydajność

TriedScarProgressPopulation 153.9
TryingPastOnesPutting 119.7
YearnGasesDeerGiven 108.3
DoubtFeetSomebodyCreature 142.5
LiquidSureDreamCatch 114.0

16
Dlaczego w przypadku przypadków testowych zmienia się entropia hasła ? Wszystkie hasła zawierające 4 słowa wygenerowane z tego samego słownika powinny mieć tę samą entropię.
NielinioweOwoc

20
Entropia hasła zależy od zestawu symboli. Jeśli twoje hasło to Nsymbole z zestawu S, entropia hasła to log2(|S|)*N. Tutaj rozmiar zestawu symboli jest rozmiarem słownika ( |S|=4284), a liczba symboli to liczba słów ( N=4), więc entropia dla każdego hasła wynosi 48.3.
NielinioweOwoc

48
Ta definicja entropii jest niebezpiecznie błędna! Jeśli każdy znak jest wybierany losowo równomiernie ze zbioru rozmiaru R, to rzeczywiście hasło o długości L ma R ^ L, więc entropia jest logiem tego: log₂ (R ^ L) = log₂ (R) * L która jest twoją formułą. Jeśli jednak hasła zostaną wybrane losowo z innego zestawu (np. Nigdy nie będziesz mieć takiego hasła 3t1ta#asd), entropia będzie logarytmem liczby możliwych haseł. Jeśli zawsze wybierasz losowo 4 słowa równomiernie ze słownika 4284-słowowego, wówczas masz 4284 ^ 4 hasła, każde z logiem entropijnym₂ (4284) * 4 ≈ 48,26.
ShreevatsaR

5
Dla przypomnienia, tego rodzaju hasła poprzedzają komiks XKCD. Nazywa się je hasłami „diceware”.
user2428118

5
Oprócz kwestii słów o mniejszej entropii niż znaków losowych, twoje pytanie wymaga, aby słowa były pisane wielkimi literami, co oznacza, że ​​wielkość liter jest ustalona i nie można ich liczyć do entropii.
Niet the Dark Absol

Odpowiedzi:


13

Python 2, 102 101 97 91 bajtów

from random import*
exec"x=''.join(x.title()for x in sample(f,4));print(x,57*len(x)/10);"*5

Przyjmuje słownik jako listę o nazwie f.

Można przetestować, zapisując plik jako dict.txti wywołując

f = open('dict.txt').readlines()

Listy w języku Python nie mają metody losowej i można zapisać dwa bajty w języku Python 2, usuwając nawiasy wokół exec( execjest słowem kluczowym w języku Python 2).
Konrad Borowski,

@xfix Tak, powinno być shuffle(f);.
Jonathan Allan

Ups, naprawiam to jak najszybciej
Martmists

4
Przydałaby się moja sztuczka polegająca na tym, że zaokrąglanie przy 5.7 jest w porządku do 1 miejsca po przecinku, o ile nie zostaną wprowadzone błędy zmiennoprzecinkowe i można zaoszczędzić pięć bajtów 57*len(x)/10.. Zapisz kolejny bajt, usuwając nawiasy, co powoduje, że wydruk zajmuje krotkę. Oto skrócona wersja: TIO
Jonathan Allan

Użyj sample(f,4)zamiast shuffle. Również fmoże to być open('dict.txt').read().split('\n'), open('dict.txt').readlines()albo po prostu open('dict.txt')(wiem, to nie grałem, ale nadal).
Alex Hall,

10

PowerShell (3.0+), 77 bajtów

1..5|%{($p=-join($d|random -c 4|%{culture|% te*|% tot* $_}));57*$p.Length/10}

Wypróbuj online!

Korzystanie Jonathan Allan „s 57*len/10podstęp.

$dzawiera słownik jako tablicę słów. Jeśli grasz w domu i chcesz wypełnić $d:

$d=-split(irm pastebin.com/raw/eMRSQ4u2)

Użycie wersji gry w golfa do użycia dużej (Get-Culture).TextInfo.ToTitleCase()litery; Nie sądzę, że istnieje krótszy sposób na to w PowerShell.

Reszta jest dość prosta.

Łącze TIO ma cały słownik; wyłącz pamięć podręczną i zwariuj!


Czy ktoś może wskazać mi odniesienie do „sztuczki Jonathana Allana 57 * len / 10”?
James Curran

@JamesCurran Zobacz podział jego odpowiedzi tutaj , a także jego komentarz do tej odpowiedzi .
briantist

To nie zadziała w wersji 2.0 poprawnej. Należy to odnotować w tytule. Myślę też, że musisz przeczytać, $dzakładając, że jest obecny w środowisku. (gc d)| random..gdzie słownik jest plikiem o nazwie d w tym samym katalogu.
Matt

1
@Matt na SO Mógłbym zrobić wszystko, aby odpowiedź działała z wersją 2 (lub zrobić 2 wersje), ale to kod golfowy! Im bardziej tajemny, tym lepiej ;-p
briantistka

1
Próbuję tylko zapisać bajty w tytułach odpowiedzi.
Matt

7

Galaretka , 22 bajty

Ẋḣ4ŒtFµL×57÷⁵⁸,K
çЀ5Y

Monadyczny link zawierający listę znaków, parsowany słownik ( dozwolony na czacie ).

Wypróbuj online! (Kliknij „Argumenty”, aby ukryć słownik i zmniejszyć potrzebę przewijania.)

W jaki sposób?

Ponieważ słownik zawiera tylko prawidłowe słowa (tylko 4znaki lub więcej [a-z]), nie ma potrzeby sprawdzania tego warunku.

Ponieważ wszystkie słowa w słowniku mają długości w [4-8]możliwych długościach hasło są w [16,32], i ewentualne entropie nigdy okrągły inaczej do jednego miejsca po przecinku niż zastępując log(52,2)z 5.7. Jedynym problemem jest to, że przy użyciu wartość zmiennoprzecinkową z 5.7da zmiennoprzecinkowych zaokrąglania błędów dla długości 18, 26oraz 31. Jednak pomnożenie przez, 57a następnie podzielenie przez 10użycie ×57÷⁵pozwala uniknąć tego (będąc nadal bajtem krótszym niż drukowanie za pomocą pełnej wartości precyzji zmiennoprzecinkowej ×52l2¤).

çЀ5Y - Main link: list of list of characters (the parsed dictionary)
   5  - literal 5
 Ѐ   - map across the implicit range [1,2,3,4,5]:
ç     -   last link (1) as a dyad
    Y - join with newlines
      - implicit print

Ẋḣ4ŒtFµL×57÷⁵⁸,K - Link 1, get password and entropy: list of lists of characters, number
Ẋ                - shuffle the list of lists (shuffle all the words)
 ḣ4              - head to 4 (the first four words)
   Œt            - title case (make the first letter of each uppercase)
     F           - flatten into one list of characters
      µ          - monadic chain separation, call that p
       L         - length of p
         57      - 57
        ×        - multiply
            ⁵    - 10
           ÷     - divide -> entropy to 1 decimal place
             ⁸   - link's left argument, p
              ,  - pair -> [p, entropy]
               K - join with (a) space(s)

5

Rubin, 89 83 bajtów

d.select!{|w|w[3]}
5.times{p w=d.sample(4).map(&:capitalize)*'',5.700439718*w.size}

Zakłada, że ​​hasła są przechowywane w zmiennej d. Możesz dodać ten wiersz przed kodem:

d=$<.map(&:chomp)

i wywołać skrypt na przykład w ten sposób:

$ ruby generate_passwords.rb < dictionary_file.txt

Przykładowe dane wyjściowe:

"MarginStarvedOnusInsulted"
142.51099295
"KitchenMiseryLurkJoints"
131.110113514
"InducesNotablePitfallsPrecede"
165.312751822
"FarmersAbortFutileWrapper"
142.51099295
"RoutesBishopGlowFaithful"
136.81055323200002

KitchenMiseryLurkJoints ... wow.


-6 bajtów z Ajedi32


1
Może być w stanie zaoszczędzić kilka bajtów poprzez usunięcie shuffle!i zastąpienie popz sample.
Ajedi32,

@ Ajedi32 Oh, masz rację! Właściwie o tym myślałem, ale źle odczytałem tę zasadę A word cannot reappear in the same password, uważając, że nie oznacza to ponownego użycia słów we wszystkich hasłach. Dzięki :)
daniero

4

Mathematica, 178 bajtów

t=1;l=Length;While[t<6,s=RandomChoice[Import["https://pastebin.com/raw/eMRSQ4u2"],4];c=Capitalize/@s;f=Flatten@Characters[c];Print[StringJoin[c]," ",Log[2,l@Union@f]*l@f//N];t++]

Wypróbuj online

skopiuj i wklej używając Ctrl-V i naciśnij Shift + Enter, aby uruchomić


Mathematica, 136 bajtów

zakładając, że m jest słownikiem, kod jest

m=ImportString[Import["C:\a.txt"]]

.

t=1;l=Length;While[t<6,s=RandomChoice[m,4];c=Capitalize/@s;f=Flatten@Characters[c];Print[StringJoin[c]," ",Log[2,l@Union@f]*l@f//N];t++]

„Dzisiaj zadaniem jest utworzenie 5 haseł za pomocą słów.” Potrzebujesz 5 zamiast jednego.
KuanHulio

ok ... 5 haseł .. naprawiono ...
J42161217

Dlaczego nie udostępniłeś słownika lokalnego, aby skrócić kod, unikając tekstu hiperłącza?
sergiol

abyś mógł go łatwo przetestować ...
J42161217

Aby ułatwić testowanie, najlepiej jest podać prosty, nieoznaczony kod pomocnika, niż w mniejszym stopniu przesłać golfa tak, aby był samodzielny. Ponadto słownik powinien być zmienny bez przechwytywania lokalnego serwera DNS (ani modyfikowania hostspliku).
wizzwizz4,

4

Bash ,66 65 bajtów

for w in `shuf -n4 -`;{((l+=${#w}));printf ${w^};};bc<<<$l*5.7004

Wypróbuj online!

Słownik jest odbierany przez STDIN. Najpierw tasuje wszystkie słowa w słowniku i wypisuje 4.

Dla każdego słowa sumuje jego długość w var l i przypomina słowo pisane wielką literą. Na koniec wzywa bc do wykonania matematyki.

Rozwiązanie awk, 112 bajtów, cztery hasła:

shuf -n16 -|xargs -n4|awk '{for(i=1;i<5;i++)printf toupper(substr($i,1,1))substr($i,2);print(length($0)-3)*5.7}'

3

(Jest to adaptacja odpowiedzi Martmistów, ale nie mam przedstawiciela, aby komentować)

Python, 88 86 bajtów

g={*f}
exec('x="".join(g.pop().title()for i in "a"*4);print(x,len(x)*5.700439718);'*5)

Wykorzystując setto, co jest niedeterministyczne, możesz uniknąć importowania bibliotek losowości.


To konsekwentnie wytwarza dla mnie tę samą moc wyjściową. Jeśli to działa na niektórych implementacjach, możesz zaoszczędzić kilka bajtów set(f).pop().
Jonathan Allan

1
Nie sądzę, żeby to było naprawdę ważne. Nie jest deterministyczne, więc nie ma gwarancji wygenerowania tego samego hasła, ale w praktyce rzadko daje różne wyniki.
DJMcMayhem

Podejrzewałem, że może to zależeć od implementacji. Zrobiłem to na świeżo zainstalowanej wersji Anaconda Python 3 dla systemu Windows i zadziałało. Jednak set(f).pop()to nie działa, próbowałem. Za każdym razem daje ten sam wynik.
Dain

„W praktyce rzadko daje różne wyniki” - wydaje mi się, oto przykład: pastebin.com/raw/ZHiHgzxV
dain

@dain Jestem ciekawy. Podaj informacje o swojej wersji Pythona.
wizzwizz4

3

Japt , 30 bajtów

5Ç[V=Uö4 ®g u +Zt1ìMm52 *Vl]¸

Wypróbuj online!


Miły! Ale niestety tworzy to samo hasło 5 razy i za każdym razem powinno być inne.
Iain Ward

Może to być 30 znaków, ale przynajmniej w UTF-8 mój system taktuje na 35 bajtach.
CVn

1
@ MichaelKjörling Japt używa ISO 8859-1, a nie UTF-8.
Dennis

@Dennis Ciekawe. Dziękuję Ci.
CVn

3

JavaScript (ES6), 164 bajty

d=>{for(i=5;i--;)console.log(p="....".replace(/./g,_=>(w=d.splice(Math.random()*d.length|0,1)[0])[0].toUpperCase()+w.slice(1)),(Math.log2(52)*p.length).toFixed(1))}

Zakłada, że ​​słownik jest przekazywany do funkcji jako tablica.

Test Snippet


2

Mathematica, 71 bajtów

Zakładając, że słownik jest już załadowany do tablicy o nazwie d.

Table[{#,Log[2,52]StringLength[#]}&[""<>Capitalize@d~RandomSample~4],5]

Wyjaśnienie:

                                        Capitalize@d                    - Capitalize all the dictionary
                                                    ~RandomSample~4     - make an array with 4 values. By default values can not repeat.
                                    ""<>                                - Concatenate with empty string to turn array into single string.
      {#,Log[2,52]StringLength[#]}&[                               ]    - Put current string next to log(2,52) times length of current string
Table[                                                              ,5] - Repeat this 5 times.

Co z numerem entropii ?!
Jonathan Allan

Ups, przeoczyło to trochę. Zaktualizowano
Ian Miller

2

ColdFusion 216 bajtów

p={};z=arrayLen(c);for(x=0;x<5;x++){pw="";r={};while(structCount(r)<4){n=RandRange(1,z);r.append({"#c[n]#":true});}for(w in structKeyList(r)){pw&=REReplace(w,"\b(\w)","\u\1","All");};p.append({"#pw#":57*len(pw)/10})}

Działa to w ColdFusion 11+ i Lucee 4.5+

Aby go uruchomić: https://trycf.com/gist/ff14e2b27d66f28ff69ab90365361b12/acf11?theme=monokai

Łącze TryCF ma mniej golfa, ale ten sam kod.

Tak naprawdę nie spodziewałem się, że będę miał konkurencyjną odpowiedź na golfa; Chciałem tylko zobaczyć, co trzeba zrobić, aby ukończyć to wyzwanie w ColdFusion. Zwłaszcza, że ​​w tych odpowiedziach nie ma dużo CF. :-) Po instalacji był zaskakująco krótszy niż się spodziewałem.

Moja pierwsza próba była trochę krótsza, dopóki nie przypomniałem sobie, że tego samego słowa nie można użyć więcej niż raz. Mimo że jest bardzo mało prawdopodobne, aby randomizator wybrał ten sam indeks więcej niż raz, wrzucam indeksy do kluczy struktury, co zapobiegnie duplikacji. Następnie używam tej listy kluczy do zbudowania mojego końcowego ciągu hasła. Użyłem też sztuczki matematycznej, aby znaleźć entropię.


2

PHP , 136 129 bajtów

-7 bajtów, dzięki Jörg

for(shuffle($a);$i++<5;){for($s='',$c=0;$c<4;)strlen($w=$a[$k++])<4?:$s.=ucfirst($w).!++$c;echo$s.' '.log(52, 2)*strlen($s)."
";}

Wypróbuj online!


@ JörgHülsermann To wydaje się działać, dzięki.
ME

2

Python 3, 252 bajty

To jest moje pierwsze wyzwanie w golfa, które wykonałem! Wiem, że są tu inne odpowiedzi w języku Python (prawdopodobnie lepsze niż moje), ale wyglądało to zabawnie, więc i tak chciałem spróbować. Oto wersja golfowa:

import random, math
with open("d") as f: d=f.read()
l=d.split()
for a in range(5):
 u=[]
 p=""
 for b in range(4):
  w=random.choice([w for w in l if not w in u and len(w)>=4])
  u.append(w)
  w=w.title()
  p+=w
 print("%s %s"%(p,math.log2(52)*len(p)))

Chciałbym opublikować Wypróbuj online! link, ale nie obsługuje wielu plików. Oto link do repl.it: https://repl.it/InIl/0

Oto też wersja bez golfisty:

import random
import math
with open("d") as f:
    dictionary = f.read() #this is the dictionary text file, simply saved as "d" as to use as few bytes as possible
words = dictionary.split() #here we turn that dictionary string into a list
for a in range(5): #here we iterate through 5 passwords
    used_words = []
    password = ""
    for b in range(4): #here we iterate through the 4 words in each password
        word = ""
        word = random.choice([word for word in words if not word in used_words and len(word) >= 4]) #Thanks to blackadder1337 from #python on freenode IRC for helping me with this.
        used_words.append(word)
        word = word.title()
        password = password + word
    print("%s %s"%(password, math.log2(52) * len(password)))

Tak jak powiedziałem, to mój pierwszy raz gofling kodu, więc jestem pewien, że można to znacznie poprawić.


Witamy w PPCG!
Taylor Scott,

2

tcl, 137

Na pewno nie zwycięzca, ale myślę, że może być trochę bardziej golfa.

time {set p "";time {set p [string totitle [lindex $d [expr int(rand()*[llength $d])]]]$p} 4;puts $p\ [expr 5.7004*[string length $p]]} 5

demo - celem pierwszego wiersza jest tylko wstawienie zawartości słownika do zmiennejd


Prawdopodobnie
możesz zagrać w

I poprosiłeś o 5 haseł zamiast 4. LOL! Niedopasowałem liczby!
sergiol

Hahaha! @sergiol
KuanHulio

Naprawiony! @KuanHulio
sergiol

Tak lepiej Dobra robota.
KuanHulio

0

Vim, 87 naciśnięć klawiszy

qq:r!echo "$RANDOM"l<CR>D:w o|e w<CR>@"ev4bd:w|bp<CR>p0~wX~wX~wX~Y:.!wc -c<CR>A*5.7003<Esc>:.!bc<CR>PJq4@q

Zakłada, że ​​słownik znajduje się w pliku o nazwie w. Zawsze będzie używać 4 kolejnych słów

Objaśnienie:

qq                       Start recording a macro named 'q'
:r!echo "$RANDOM"l<CR>   Append the result of the shell command `echo "$RANDOM"l`
D                        Delete what you just appended
:w o|                    Save the buffer to the file 'o' and ..
e w<CR>                  Open the file 'w'
@"                       Execute the text we deleted as a normal-mode command
                         This will move the cursor a random number of characters
                         to the right
e                        Go to the end of the next word
v4bd                     Delete 4 words backwards
:w|                      Save the file and ..
bp<CR>                   Open the last buffer (the 'o' file)
p                        Paste the 4 words we deleted
0                        Move the cursor to the beginning of the line
~wX~wX~wX~               Remove the spaces between the words and capitalize
Y                        Copy current line
:.!wc -c<CR>             Pipe the current line through 'wc -c'
A*5.7003<Esc>            Append "*5.7003" to the end of the line
:.!bc<CR>                Pipe the current line through 'bc'
P                        Paste the password above the current line
J                        Join with line bellow
q                        Stop recording the 'q' macro
4@q                      Run the 'q' macro 4 times

0

q / kdb +, 76 74 65 56 bajtów

Rozwiązanie:

{(x;5.70044*(#)x)}(,/)@[;0;upper]each -4?" "vs(*)(0:)`:w

Przykład:

q){(x;5.70044*(#)x)}(,/)@[;0;upper]each -4?" "vs(*)(0:)`:w
"RulingOverheadSaddensPriest"
153.9119

Wyjaśnienie:

Przeczytaj listę słów, rozdziel „”, wybierz 4 losowe słowa z tej listy, pierwszą literę każdego słowa, a następnie połącz ze sobą. Wprowadź to do funkcji lambda, która zwraca hasło i obliczoną „entropię”:

                                                     `:w / the wordlist is a file called 'w'
                                                 (0:)    / read in the file list (\n separated list)
                                              (*)        / take first (and only) item in the list
                                         " "vs           / split this on " "
                                      -4?                / take 4 random items from this list, neg means 'dont put back'
                      @[; ;     ]                        / apply a function to variable at indices (variable is implicit)
                           upper                         / uppercase (the function being applied)
                         0                               / index 0, the first character
                                 each                    / each of the 4 random items
                  (,/)                                   / 'raze' (flatten lists)
{                }                                       / anonymous lambda function
 (x;            )                                        / a 2-item list, x is first item
            (#)x                                         / count x, return the length of the list
    5.70044*                                             / multiply by 5.70044

Uwagi:

Wpadłem i użyłem 5.70044 zamiast 2 xlog 52 xexp...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.