XKCD密码生成器


34

介绍

显然,此问题已在此处提出,但很遗憾已关闭。我认为再试一次是个好主意,但操作正确。

XKCD认为我们是如何使用“难以记住的密码”的培训的,认为它是安全的,但是要花3天的时间才能破解计算机。另一方面,记住4到5个单词会使Kuan的Password Intropy变得更容易记住。疯狂如何运作,是吧?

挑战

今天的工作是使用单词创建5个密码。每个密码4个单词,每个单词至少4个字母,但没有最大限制。需要为每个密码计算Kuan的密码熵,但不会设置强制最小值。

什么是关的密码熵?

Kuan说,Kuan的密码熵是对密码不可预测性的一种度量。有一个简单的计算:E =日志2(R)* L。E是关的密码熵,R是可用字符的范围,L是密码长度。

可用字符的范围不言自明。密码可以包含的字符范围,在这种情况下为大写和小写。由于字母中有26个字符,因此在整个密码范围内26 x 2 = 52个字符。

密码长度也是不言自明的。它是创建后密码的总长度。

约束条件

  • 没有输入。
  • 单词不能再次出现在相同的密码中。
  • 密码中不允许使用符号或数字。
  • 每个密码4个字,但每个字至少4个字母。
  • 单词之间没有空格。
  • 您不能一遍又一遍地生成相同的密码。
  • 每个单词都必须用密码大写。
  • 输出必须是人类可读的,必须隔开。还必须使用上面的Kuan's Password Intropy等式将密码的Kuan's Password Intropy包括在内。
  • 字典。您必须使用它,将其下载为文本文件并进行相应集成。这将是您从中获取单词的列表。您的代码应假定其可用。
  • 这是,最短字节获胜。

输出量

TriedScarProgressPopulation 153.9
TryingPastOnesPutting 119.7
YearnGasesDeerGiven 108.3
DoubtFeetSomebodyCreature 142.5
LiquidSureDreamCatch 114.0

16
对于测试用例,为什么密码熵会变化?从同一词典生成的所有4个单词的密码应具有相同的熵。
NonlinearFruit

20
密码熵取决于符号集。如果密码是N集合中的符号S,则密码熵为log2(|S|)*N。这里符号集的大小是字典的大小(|S|=4284),符号数是单词的数量(N=4),因此每个密码的熵是48.3
NonlinearFruit

48
熵的这种定义是危险的错误!如果从一组大小为R的随机字符中均匀选择每个字符,那么长度为L的密码确实具有R ^ L的可能性,因此熵就是对数:log 2(R ^ L)= log 2(R)* L这是你的公式。但是,如果密码是从不同的集合中随机选择的(例如,您永远不会有像这样的密码3t1ta#asd),那么熵将是可能的密码数量的对数。如果您总是从4284个单词的词典中随机选择4个单词,那么会有4284 ^ 4个密码,每个密码的熵为log 2(4284)* 4≈48.26。
ShreevatsaR

5
作为记录,这种密码早于XKCD漫画。它们被称为“骰子”密码。
user2428118

5
除了单词熵比随机字符少的问题外,您的问题还要求单词大写,这意味着大小写是固定的,不能计算为熵。
Niet the Dark Absol's

Answers:


13

Python 2,102 101 97 91字节

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

假定字典为名为的列表f

可以通过将文件另存为dict.txt并调用来进行测试

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

Python列表没有shuffle方法,您可以通过删除括号execexec在Python 2中为关键字)在Python 2中节省两个字节。
Konrad Borowski

@xfix是的shuffle(f);
乔纳森·艾伦

哎呀,那固定尽快
Martmists

4
您可以使用我的技巧来指出,只要不引入浮点错误,就可以将5.7的舍入舍入到小数点后1位,并使用保存5个字节57*len(x)/10.。通过删除括号使打印结果变为元组,以节省另一个字节。以下是精简版:TIO
Jonathan Allan

使用sample(f,4)代替shuffle。也f可以只是open('dict.txt').read().split('\n')open('dict.txt').readlines()或只是open('dict.txt')(我知道这不是golfed但仍然)。
亚历克斯·霍尔

10

PowerShell(3.0 +),77字节

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

在线尝试!

使用乔纳森·艾伦57*len/10把戏。

$d包含字典作为单词数组。如果您在家里玩并且想填补$d

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

使用高尔夫球版(Get-Culture).TextInfo.ToTitleCase()的首字母大写;我认为在PowerShell中没有更短的方法可以做到这一点。

我认为其余的内容非常简单。

TIO链接包含整个词典;禁用缓存并发疯!


有人可以指出“乔纳森·艾伦的57 * len / 10技巧”吗?
James Curran

@JamesCurran 在此处查看他的答案的细目分类,以及他对此答案的评论。
briantist

在2.0版本中,这将无法正常工作。应该在标题中注明。我还认为您需要$d像假定环境中存在的那样进行阅读。(gc d)| random..其中字典是同一目录中名为d的文件。
马特

1
@Matt on SO我可能会竭尽全力使答案与v2一起工作(或做2个版本),但这是代码高尔夫!奥秘程度越高越好
;-p

1
我只是想在我的答案标题中保存字节。
马特

7

果冻,22 字节

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

一个单子链接,其中包含字符列表,已解析的字典(在chat中允许)。

在线尝试!(单击“参数”以隐藏字典并减少滚动的需要。)

怎么样?

由于字典仅包含有效词(仅包含4字符或更多[a-z]),因此无需检查此条件。

由于所有的字典中的单词在长度[4-8]可能的密码长度在[16,32],并可能熵永远一轮不同的到小数点后一位比替换log(52,2)5.7。唯一的问题是,使用的浮点值5.7将为浮点舍入误差的长度182631。然而,通过乘以57然后除以10使用×57÷⁵避免了这种(同时仍然字节比打印使用全浮点精度值短×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

Ruby,89 83个字节

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

假定密码存储在变量中d。您可以在代码之前添加以下行:

d=$<.map(&:chomp)

并像下面这样调用脚本:

$ ruby generate_passwords.rb < dictionary_file.txt

样本输出:

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

KitchenMiseryLurkJoints ...哇。


来自Ajedi32的-6个字节


1
或许可以通过删除保存的几个字节shuffle!和更换popsample
Ajedi32

@ Ajedi32哦,对了!我实际上是在想,但是我误读了这个规则A word cannot reappear in the same password,认为它意味着在所有密码中不能重复使用单词。谢谢:)
daniero '17

4

Mathematica,178个字节

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++]

在线尝试

使用ctrl-v复制并粘贴,然后按shift + enter运行


Mathematica,136个字节

假设m是字典,代码是

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++]

“今天的工作是使用单词创建5个密码。”需要5个而不是1个。
KuanHulio

好的... 5个密码..已修复..
J42161217

您为什么不通过避免超链接文本使字典在本地可用以缩短代码?
sergiol

为了方便您进行测试...
J42161217 '17

最好提供简单,简单易用的帮助程序代码,以简化测试,而不是减少提交的内容,使其自成体系。同样,该字典应该是可变的,而不会劫持本地DNS服务器(或修改hosts文件)。
wizzwizz4

4

66 65字节

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

在线尝试!

字典由STDIN接收。随机整理字典中的所有单词,然后先输出4。

对于每个单词,将其长度加到var l中,并回显大写单词。最后调用bc进行数学运算。

AWK解决方案,112字节,四个密码:

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

(这是Martmists的回答的改编,但我没有代表对此发表评论)

Python,88 86字节

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

通过利用set不确定性的方式,您可以避免导入任何随机性库。


这始终为我产生相同的输出。如果它在某些实现上起作用,那么你可以通过做一些节省字节set(f).pop()
乔纳森·艾伦,

1
我认为这不是真的。它不是确定性的,因此不能保证产生相同的密码,但是实际上它很少会产生不同的结果。
DJMcMayhem

我怀疑这可能取决于实现。我是在新安装的Windows版本的Anaconda Python 3上完成的,并且可以正常工作。但是set(f).pop()不起作用,我尝试了。每次给出相同的结果。
dain

“在实践中,它很少会产生不同的结果” –对我来说,这是一个示例:pastebin.com/raw/ZHiHgzxV
dain

@dain我很好奇。请提供有关您的Python构建的信息。
wizzwizz4

3

Japt,30个字节

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

在线尝试!


真好!但不幸的是它创建相同的密码5次,它应该是不同的,每次..
恩·沃德

这可能是30个字符,但至少在UTF-8中,我的系统使用35个字节作为时钟。
CVn

1
@MichaelKjörlingJapt使用ISO 8859-1,而不是UTF-8。
丹尼斯

@丹尼斯有趣。谢谢。
CVn

3

JavaScript(ES6),164个字节

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

假定字典以数组形式传递给函数。

测试片段


2

Mathematica,71字节

假设字典已加载到名为的数组中d

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

说明:

                                        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.

熵数呢?
乔纳森·艾伦

糟糕,错过了这一点。更新。
伊恩·米勒

2

ColdFusion 216字节

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

这适用于ColdFusion 11+和Lucee 4.5+

要运行它:https : //trycf.com/gist/ff14e2b27d66f28ff69ab90365361b12/acf11?theme=monokai

TryCF链接的代码不那么实用,但是代码相同。

我真的没想到会有一个有竞争力的高尔夫答案。我只是想看看在ColdFusion中完成此挑战将需要什么。特别是因为这些答案中没有太多CF。:-)设置之后,它比我预期的短得多。

我的第一次尝试要短一些,直到我想起同一单词不能多次使用。即使随机数选择同一索引的可能性很小,但我还是将索引转储到结构的键中,以防止重复。然后,我使用该键列表来构建最终的密码字符串。我还使用数学技巧来找到熵。


2

PHP136 129字节

-7个字节,谢谢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)."
";}

在线尝试!


@JörgHülsermann似乎行得通,谢谢。
ME

2

Python 3,252个字节

这是我完成的第一次高尔夫挑战代码!我知道这里还有其他Python答案(可能比我的要好),但这看起来很有趣,所以我还是想尝试一下。这是高尔夫球版:

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

我会在网上发布试用!链接,但不支持多个文件。所以这是一个repl.it链接:https ://repl.it/InIl/0

另外,这是非高尔夫版本:

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

就像我说的那样,这是我第一次使用代码,所以我敢肯定,可以做很多改进。


欢迎来到PPCG!
泰勒·斯科特

2

tcl,137

当然不是赢家,但我认为可以打更多一点。

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 —第1行的目的只是将字典内容放入变量中d


你也许可以打高尔夫球下来,因为密码需要4个字,而不是5
KuanHulio

您要求输入5个密码而不是4个。大声笑!我不匹配数字!
sergiol

哈哈哈!@sergiol
KuanHulio

固定!@KuanHulio
sergiol

这样更好 不错的工作。
KuanHulio '17

0

Vim,87次击键

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

假设字典在名为的文件中w。将始终使用4个连续的单词

说明:

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字节

解:

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

例:

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

说明:

阅读单词列表,在“”上分开,从列表中选择4个随机单词,每个单词的首字母大写,然后合并在一起。将其输入到lambda函数中,该函数返回密码和计算出的“熵”:

                                                     `: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

笔记:

我陷进去并使用5.70044而不是2 xlog 52 xexp...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.