可发布的哈希函数挑战


73

在此您将使用140字节1或更少的源代码编写一个哈希函数。哈希函数必须将ASCII字符串作为输入,并返回24位无符号整数([0,2 24 -1])作为输出。

您的哈希函数将针对这本大型英式英语词典2中的每个单词进行评估。您的分数是与另一个单词共享一个哈希值(冲突)的单词数量。

得分最低者获胜,第一张海报打破了平局。

测试用例

提交之前,请在以下输入中测试评分脚本:

duplicate
duplicate
duplicate
duplicate

如果它给出的分数不是4,那就是越野车。


澄清规则:

  1. 您的哈希函数必须在单个字符串上而不是整个数组上运行。另外,您的哈希函数除了输入字符串和输出整数外,可能不执行任何其他I / O操作。
  2. 不允许使用内置的哈希函数或类似功能(例如,加密加扰字节)。
  3. 您的哈希函数必须是确定性的。
  4. 与大多数其他竞赛相反,允许专门针对得分输入进行优化。

1我知道Twitter限制字符而不是字节,但为简单起见,我们将字节作为对此挑战的限制。
2从Debian的wbritish-huge修改而来,删除了所有非ASCII单词。


11
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch's?什么...
Luis Mendo

8
@DonMuesli en.wikipedia.org/wiki/Llanfairpwllgwyngyll(有趣的事实:这个词也在Jelly的内置压缩字典中)
Martin Ender

8
我认为您不应该使用内置字典。
丹尼斯

4
供参考:以SHA-512的24 MSB将实现的得分为6816.
丹尼斯

10
一些后端计算:对于D=340275单词和R=2^24哈希输出,随机哈希具有预期的D^2/(2*R) = 3450冲突对,其中一些重叠。有一个预期的D^3/(6*R^2) = 23碰撞三元组,并且较大碰撞的数量可以忽略不计,这意味着这些三元组很可能是不相交的。这给出了一个预期的6829词,它们共享一个散列值,〜70为三倍,其余为成对。标准偏差估计为118,因此<6200使用随机散列获取大约是5 sigma事件。
xnor

Answers:


11

好的,我去学习高尔夫球语言。

CJam,140个字节,3314个冲突词

00000000: 7b5f 3162 225e d466 4a55 a05e 9f47 fc51  {_1b"^.fJU.^.G.Q
00000010: c45b 4965 3073 72dd e1b4 d887 a4ac bcbd  .[Ie0sr.........
00000020: 9c8f 70ca 2981 b2df 745a 10d0 dfca 6cff  ..p.)...tZ....l.
00000030: 7a3b 64df e730 54b4 b068 8584 5f6c 9f6b  z;d..0T..h.._l.k
00000040: b7f8 7a1f a2d3 b2b8 bcf5 cfa6 1ef7 a55c  ..z............\
00000050: dca8 795c 2492 dc32 1fb6 f449 f9ca f6b7  ..y\$..2...I....
00000060: a2cf 4772 266e ad4f d90c d236 b51d c5d5  ..Gr&n.O...6....
00000070: 5c46 3f9b 7cb4 f195 4efc fe4a ce8d 9aee  \F?.|...N..J....
00000080: 9dbc 223d 6962 3443 2329 257d            .."=ib4C#)%}

定义一个块(匿名函数)。为了进行测试,您可以添加qN%%N*N在stdin上获取以换行符分隔的单词列表,并在stdout上编写以换行符分隔的哈希值列表。等效的Python代码:

b=lambda s,a:reduce(lambda n,c:n*a+ord(c),s,0)
f=lambda s:b(s,ord('^\xd4fJU\xa0^\x9fG\xfcQ\xc4[Ie0sr\xdd\xe1\xb4\xd8\x87\xa4\xac\xbc\xbd\x9c\x8fp\xca)\x81\xb2\xdftZ\x10\xd0\xdf\xcal\xffz;d\xdf\xe70T\xb4\xb0h\x85\x84_l\x9fk\xb7\xf8z\x1f\xa2\xd3\xb2\xb8\xbc\xf5\xcf\xa6\x1e\xf7\xa5\\\xdc\xa8y\\$\x92\xdc2\x1f\xb6\xf4I\xf9\xca\xf6\xb7\xa2\xcfGr&n\xadO\xd9\x0c\xd26\xb5\x1d\xc5\xd5\\F?\x9b|\xb4\xf1\x95N\xfc\xfeJ\xce\x8d\x9a\xee\x9d\xbc'[b(s,1)%125]))%(8**8+1)

Pyth,140个字节,3535 3396个冲突单词

00000000: 4c25 4362 2d68 5e38 2038 2a36 3643 4022  L%Cb-h^8 8*66C@"
00000010: aa07 f29a 27a7 133a 3901 484d 3f9b 1982  ....'..:9.HM?...
00000020: d261 79ab adab 9d92 888c 3012 a280 76cf  .ay.......0...v.
00000030: a2e5 8f81 7039 acee c42e bc18 28d8 efbf  ....p9......(...
00000040: 0ebe 2910 9c90 158e 3742 71b4 bdf5 59c2  ..).....7Bq...Y.
00000050: f90b e291 8673 ea59 6975 10be e750 84c8  .....s.Yiu...P..
00000060: 0b0f e7e8 f591 f628 cefa 1ab3 2e3c 72a3  .......(.....<r.
00000070: 7f09 6190 dbd2 d54e d6d0 d391 a780 ebb6  ..a....N........
00000080: ae86 2d1e 49b0 552e 7522 4362            ..-.I.U.u"Cb

定义名为的函数y。为了进行测试,您可以添加jmyd.z在stdin上获取以换行符分隔的单词列表,并在stdout上编写以换行符分隔的哈希值列表。等效的Python代码:

b=lambda s,a:reduce(lambda n,c:n*a+ord(c),s,0)
f=lambda s:b(s,256)%(8**8+1-66*ord("\xaa\x07\xf2\x9a'\xa7\x13:9\x01HM?\x9b\x19\x82\xd2ay\xab\xad\xab\x9d\x92\x88\x8c0\x12\xa2\x80v\xcf\xa2\xe5\x8f\x81p9\xac\xee\xc4.\xbc\x18(\xd8\xef\xbf\x0e\xbe)\x10\x9c\x90\x15\x8e7Bq\xb4\xbd\xf5Y\xc2\xf9\x0b\xe2\x91\x86s\xeaYiu\x10\xbe\xe7P\x84\xc8\x0b\x0f\xe7\xe8\xf5\x91\xf6(\xce\xfa\x1a\xb3.<r\xa3\x7f\ta\x90\xdb\xd2\xd5N\xd6\xd0\xd3\x91\xa7\x80\xeb\xb6\xae\x86-\x1eI\xb0U.u"[b(s,256)%121]))

理论极限

我们期望做得如何?这是x(冲突单词的数量)与y(获得最多x个冲突单词所需的熵,以字节为单位)的关系图。例如,点(2835,140)告诉我们,随机函数最多可以以1/256 ** 140的概率获得2835个碰撞词,因此我们不可能比140更好地做得更好代码字节。

图形


很好地分析了理论极限。为了突破这一理论极限,可能必须使用一种语言,该语言具有针对问题中的词典进行了优化的内置功能(可能会作弊)。如果该语言具有内置的加密哈希,则可以将限制变成或多或少的建设性方法,以寻找最佳解决方案。考虑一下:$ h(w || c)%2 ^ {24} $其中$ c $是字节字符串常量。在随机预言模型中,可以证明它很可能接近最佳值。当然,强行强制$ c $是不可行的。
kasperd '16

您如何计算图形的公式?非常有意思!
NikoNyrh

@NikoNyrh动态编程。令(wch)表示具有w个词的状态,其中ch个不同的哈希冲突,其余的w - c均具有不同的哈希。如果我们添加一个随机词,则状态变为(w + 1,ch)的概率为1-(h + w - c)/ 2 ^ 24,或者为(w + 1,c + 1,h)的概率为h / 2 ^ 24,或(w + 1,c+ 2,h + 1)的概率为(wc)/ 2 ^ 24。然后用图形化最终熵X碰撞词语是在状态(340275,概率的总和的数底数1/256 Çħ)与ÇX
安德斯·卡塞格

我不敢相信没有人问过您如何提出哈希函数?我很想知道。
阿努什

22

蟒蛇,5333 4991

我相信这是第一个得分大大超过随机预言家的竞争者。

def H(s):n=int(s.encode('hex'),16);return n%(8**8-ord('+%:5O![/5;QwrXsIf]\'k#!__u5O}nQ~{;/~{CutM;ItulA{uOk_7"ud-o?y<Cn~-`bl_Yb'[n%70]))

1
巫术!def H(s):n=int(s.encode('hex'),16);return n%...保存5个字节,以防万一您可以以某种方式使用它们……
Dennis

3
@Dennis我本可以使用5个字节来使字符串常量增加5个字节。但是,如果我更改长度,则必须从头开始构建字符串常量。而且我不确定这5个字节是否会给我带来足够的改进,是否值得从构建字符串开始。我已经花了数小时的CPU时间来优化字符串常量。
kasperd '16

@Dennis我想一些额外的字节可以让我自由地在需要转义的常量中使用某些字符。这样,我可以潜在地使用一些额外的字节,而不必重新构造字符串。
kasperd '16

7
如果要另一个字节,2**24 == 8**8
Anders Kaseorg '16

20

Python 2,140个字节,4266个冲突单词

鉴于不可打印字节的可读写性,我真的不想从不可打印的字节开始,但是好吧,我没有开始。:-P

00000000: efbb bf64 6566 2066 2873 293a 6e3d 696e  ...def f(s):n=in
00000010: 7428 732e 656e 636f 6465 2827 6865 7827  t(s.encode('hex'
00000020: 292c 3336 293b 7265 7475 726e 206e 2528  ),36);return n%(
00000030: 382a 2a38 2b31 2d32 3130 2a6f 7264 2827  8**8+1-210*ord('
00000040: 6f8e 474c 9f5a b49a 01ad c47f cf84 7b53  o.GL.Z........{S
00000050: 49ea c71b 29cb 929a a53b fc62 3afb e38e  I...)....;.b:...
00000060: e533 7360 982a 50a0 2a82 1f7d 768c 7877  .3s`.*P.*..}v.xw
00000070: d78a cb4f c5ef 9bdb 57b4 7745 3a07 8cb0  ...O....W.wE:...
00000080: 868f a927 5b6e 2536 375d 2929            ...'[n%67]))

Python 2,140个可打印字节,4662 4471 4362冲突单词

def f(s):n=int(s.encode('hex'),16);return n%(8**8+3-60*ord('4BZp%(jTvy"WTf.[Lbjk6,-[LVbSvF[Vtw2e,NsR?:VxC0h5%m}F5,%d7Kt5@SxSYX-=$N>'[n%71]))

显然是受卡巴斯德解决方案形式的启发,但重要的是在模量空间上增加了仿射变换,以及完全不同的参数。


+1我不会不战而放弃。但是我认为我必须停止优化当前的解决方案,而找到另一种方法,因为如果我继续使用当前的方法来优化参数,那么我不会击败您。我会回来与编辑我的解决办法,一旦我打败你....
kasperd

@kasperd:太好了,快来吧。:-P
Anders Kaseorg '16

1
@AndersKaseorg您如何找到字符串?
ASCII码,仅ASCII

@AndersKaseorg我设法大大加快了参数搜索的速度。然后,我消除了一个障碍,该障碍使我的搜索陷入了次优解决方案的困境。但是我仍然无法比4885做得更好。在思考了为什么我无法再做任何事情之后,我突然意识到我的解决方案出了什么问题以及如何解决。现在,您的解决方案中的仿射变换对我来说非常有意义。我认为我唯一能赶上的方法就是自己使用仿射变换。
kasperd '16

1
@kasperd:非常好。当在n%(8**8-ord('…'[n%70]))不更改其他参数的情况下搜索更好的字符串时,我只能设法达到4995,因此看起来您的新优化程序已经被我所接受。现在,这变得更加有趣!
Anders Kaseorg '16

16

果酱,4125 3937 3791 3677

0000000: 7b 5f 39 62 31 31 30 25 5f 22 7d 13 25 77  {_9b110%_"}.%w
000000e: 77 5c 22 0c e1 f5 7b 83 45 85 c0 ed 08 10  w\"...{.E.....
000001c: d3 46 0c 5c 22 59 f8 da 7b f8 18 14 8e 4b  .F.\"Y..{....K
000002a: 3a c1 9e 97 f8 f2 5c 18 21 63 13 c8 d3 86  :.....\.!c....
0000038: 45 8e 64 33 61 50 96 c4 48 ea 54 3b b3 ab  E.d3aP..H.T;..
0000046: bc 90 bc 24 21 20 50 30 85 5f 7d 7d 59 2c  ...$! P0._}}Y,
0000054: 4a 67 88 c8 94 29 1a 1a 1a 0f 38 c5 8a 49  Jg...)....8..I
0000062: 9b 54 90 b3 bd 23 c6 ed 26 ad b6 79 89 6f  .T...#..&..y.o
0000070: bd 2f 44 6c f5 3f ae af 62 9b 22 3d 69 40  ./Dl.?..b."=i@
000007e: 62 31 35 32 35 31 39 25 31 31 30 2a 2b 7d  b152519%110*+}

这种方法将域和共域划分为110个不相交的集合,并为每对定义了稍微不同的哈希函数。

评分/验证

$ echo $LANG
en_US
$ cat gen.cjam
"qN%{_9b110%_"
[125 19 37 119 119 34 12 225 245 123 131 69 133 192 237 8 16 211 70 12 34 89 248 218 123 248 24 20 142 75 58 193 158 151 248 242 92 24 33 99 19 200 211 134 69 142 100 51 97 80 150 196 72 234 84 59 179 171 188 144 188 36 33 32 80 48 133 95 125 125 89 44 74 103 136 200 148 41 26 26 26 15 56 197 138 73 155 84 144 179 189 35 198 237 38 173 182 121 137 111 189 47 68 108 245 63 174 175 98 155]
:c`"=i@b152519%110*+}%N*N"
$ cjam gen.cjam > test.cjam
$ cjam test.cjam < british-english-huge.txt | sort -n > temp
$ head -1 temp
8
$ tail -1 temp
16776899
$ all=$(wc -l < british-english-huge.txt)
$ unique=$(uniq -u < temp | wc -l)
$ echo $[all - unique]
3677

以下Python端口可与官方评分片段一起使用:

h=lambda s,b:len(s)and ord(s[-1])+b*h(s[:-1],b)

def H(s):
 p=h(s,9)%110
 return h(s,ord(
  '}\x13%ww"\x0c\xe1\xf5{\x83E\x85\xc0\xed\x08\x10\xd3F\x0c"Y\xf8\xda{\xf8\x18\x14\x8eK:\xc1\x9e\x97\xf8\xf2\\\x18!c\x13\xc8\xd3\x86E\x8ed3aP\x96\xc4H\xeaT;\xb3\xab\xbc\x90\xbc$! P0\x85_}}Y,Jg\x88\xc8\x94)\x1a\x1a\x1a\x0f8\xc5\x8aI\x9bT\x90\xb3\xbd#\xc6\xed&\xad\xb6y\x89o\xbd/Dl\xf5?\xae\xafb\x9b'
  [p]))%152519*110+p

1
我已将代码移植到Python,以方便验证。
丹尼斯

h在该Python端口中是否对应于内置的CJam?
kasperd '16

是。这是CJam的b(基本转换)。
丹尼斯

您的计分过程是bash吗?
GamrCorps '16

@GamrCorps是的,那是Bash。
丹尼斯

11

蟒蛇,6446 6372


此解决方案实现的冲突计数比以前的所有条目都要低,并且仅需要代码所允许的140个字节中的44个:

H=lambda s:int(s.encode('hex'),16)%16727401

2
@ mbomb007 orlp自己提交的内容确实%(2**24-1)如此,因此我认为要求澄清可能会很好
Sp3000 '16

12
@ mbomb007挑战表明没有这种事情。它表示该函数必须采用ASCII字符串作为输入,并输出该范围内的整数。无论您为我的功能提供哪个输入,输出都将在该范围内。单词函数的数学定义并不要求它产生所有允许的输出。如果这就是您想要的数学术语,那就是射影函数。但是在要求中未使用“排斥”一词。
kasperd '16

@ mbomb007:不需要哈希函数是射影。例如,由于内存对齐,许多基于内存地址的哈希函数只能产生2的小数幂的倍数,包括旧版本Python中的默认对象哈希。许多散列函数甚至具有比共域更小的域,因此无论如何它们都不是排斥的。
user2357112 '16

3
@ mbomb007-实际上,由于数字值远远[0, 2**24-1]大于英语中的单词,因此从数学上讲不可能在该范围内的每个值都可行的情况下进行哈希运算。
Darrel Hoffman

7

果酱,6273

{49f^245b16777213%}

将每个字符与49进行 XOR运算,通过x,y x 245x + y减少结果字符串,并取模余数16,777,213(最大的24位素数)。

计分

$ cat hash.cjam
qN% {49f^245b16777213%} %N*N
$ all=$(wc -l < british-english-huge.txt)
$ unique=$(cjam hash.cjam < british-english-huge.txt | sort | uniq -u | wc -l)
$ echo $[all - unique]
6273

我根据您的描述在python中重新实现了该算法。我可以确认您的分数已通过官方分数计算核对。
kasperd '16

7

JavaScript(ES6),6389

哈希函数(105字节):

s=>[...s.replace(/[A-Z]/g,a=>(b=a.toLowerCase())+b+b)].reduce((a,b)=>(a<<3)*28-a^b.charCodeAt(),0)<<8>>>8

计分功能(NodeJS)(170字节):

h={},c=0,l=require('fs').readFileSync(process.argv[2],'utf8').split('\n').map(a=>h[b=F(a)]=-~h[b])
for(w of Object.getOwnPropertyNames(h)){c+=h[w]>1&&h[w]}
console.log(c)

调用as node hash.js dictionary.txt,其中hash.js是脚本,dictionary.txt是字典文本文件(不带最后的换行符),并且F被定义为哈希函数。

感谢Neil减少了散列函数的9个字节!


为什么要分配给?另外,((...)>>>0)%(1<<24)也可以使用代替(...)<<8>>>8
Neil

@Neil因为字母,而我很无聊= P另外,按位数学也很好!那节省了7个字节=)
Mwr247 '16

不错,这不是编码高尔夫球,否则我也不得不为您使用未使用的变量i
尼尔

@Neil Crap> _ <我在测试一些其他哈希想法时忘了使用它,却忘了删除XD是的,这可不是好事,尽管同样,如果我可以压缩哈希和计分功能,我会很喜欢分成相同的140个字节,因此每一位都有帮助;)
Mwr247 '16

1
@ Sp3000 Gah,我明白你的意思了。发现碰撞时,矿井最初并未计算那里的人员。我会解决的。
Mwr247 '16

5

Mathematica,6473

下一步...在将它们取模2 24之前,我们不将字符代码求和而不是将它们视为基数151的数字。

hash[word_] := Mod[FromDigits[ToCharacterCode @ word, 151], 2^24]

以下是确定冲突次数的简短脚本:

Total[Last /@ DeleteCases[Tally[hash /@ words], {_, 1}]]

我刚刚从头1开始系统地尝试了所有基地,到目前为止,基地151发生的碰撞最少。我会再尝试一些尝试以进一步降低分数,但是测试有点慢。


5

Javascript(ES5),6765

这是CRC24缩减为140字节。可以打更多的球,但是想得到我的答案:)

function(s){c=0xb704ce;i=0;while(s[i]){c^=(s.charCodeAt(i++)&255)<<16;for(j=0;j++<8;){c<<=1;if(c&0x1000000)c^=0x1864cfb}}return c&0xffffff}

node.js中的验证器:

var col = new Array(16777215);
var n = 0;

var crc24_140 = 
function(s){c=0xb704ce;i=0;while(s[i]){c^=(s.charCodeAt(i++)&255)<<16;for(j=0;j++<8;){c<<=1;if(c&0x1000000)c^=0x1864cfb}}return c&0xffffff}

require('fs').readFileSync('./dict.txt','utf8').split('\n').map(function(s){ 
    var h = crc24_140(s);
    if (col[h]===1) {
        col[h]=2;
        n+=2;
    } else if (col[h]===2) {
        n++;
    } else {
        col[h]=1;
    }
});

console.log(n);

欢迎来到编程难题和代码高尔夫球!
Alex A.

...感谢您对@AlexA的热烈欢迎!
binarymax '16

5

蟒蛇,340053

这是可怕的算法得出的可怕成绩,这个答案的存在还多于给出一个显示评分的小型Python脚本。

H=lambda s:sum(map(ord, s))%(2**24)

得分:

hashes = []
with open("british-english-huge.txt") as f:
    for line in f:
        word = line.rstrip("\n")
        hashes.append(H(word))

from collections import Counter
print(sum(v for k, v in Counter(hashes).items() if v > 1))

1
让评分代码断言哈希函数的返回值是允许范围内的整数可能会很有用。
kasperd '16

4

蟒蛇,6390 6376 6359

H=lambda s:reduce(lambda a,x:a*178+ord(x),s,0)%(2**24-48)

可以认为是对MartinBüttner的回答的微不足道的修改。


3
@ mbomb007这不是事实。如果您的函数始终输出4,则仍在range范围内输出[0, 2**24-1]。唯一不允许的事情是输出不在该范围内的任何数字,例如-12**24
orlp 2016年

3

Python,9310


是的,不是最好的,但至少是这样。正如我们在crypto中所说,永远不要编写自己的哈希函数

也正好是140个字节长。

F=lambda x,o=ord,m=map:int((int(''.join(m(lambda z:str(o(z)^o(x[-x.find(z)])^o(x[o(z)%len(x)])),x)))^(sum(m(int,m(o,x))))^o(x[-1]))%(2**24))

2

Matlab,30828 8620 6848

它通过为每个ascii字符/位置组合分配一个质数并为每个单词取小于2 ^ 24的最大质数取模来计算其乘积,从而构建哈希。请注意,为了进行测试,我在while循环之前将对素数的调用直接移到了测试器之外,并将其传递给了散列函数,因为它使它的速度提高了大约1000倍,但是该版本有效并且是独立的。如果单词长度超过40个字符,则可能会崩溃。

function h = H(s)
p = primes(1e6);
h = 1;
for i=1:length(s)
    h = mod(h*p(double(s(i))*i),16777213);
end
end

测试人员:

clc
clear variables
close all

file = fopen('british-english-huge.txt');
hashes = containers.Map('KeyType','uint64','ValueType','uint64');

words = 0;
p = primes(1e6);
while ~feof(file)
    words = words + 1;
    word = fgetl(file);
    hash = H(word,p);
    if hashes.isKey(hash)
        hashes(hash) = hashes(hash) + 1;
    else
        hashes(hash) = 1;
    end
end

collisions = 0;
for key=keys(hashes)

    if hashes(key{1})>1
        collisions = collisions + hashes(key{1});
    end
end

如果要节省程序空间,则不必将char double显式转换为。您也可以使用numel而不是length。虽然不确定所有这些额外的字节将如何处理!
Suever 2016年

1

Ruby,9309次碰撞,107个字节

def hash(s);require'prime';p=Prime.first(70);(0...s.size).reduce(0){|a,i|a+=p[i]**(s[i].ord)}%(2**24-1);end 

这不是一个好的竞争者,但我想探索与其他参赛作品不同的想法。

将前n个质数分配给字符串的前n个位置,然后将所有质数[i] **(string [i]的ascii码)相加,然后加上mod 2 ** 24-1。


1

Java 8、7054 6467

这是受内置java.lang.String.hashCode函数的启发(但不能从中复制),因此请根据规则2随意禁止。

w -> { return w.chars().reduce(53, (acc, c) -> Math.abs(acc * 79 + c)) % 16777216; };

得分:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class TweetableHash {
    public static void main(String[] args) throws Exception {
        List<String> words = Files.readAllLines(Paths.get("british-english-huge.txt"));

        Function<String, Integer> hashFunc = w -> { return w.chars().reduce(53, (acc, c) -> Math.abs(acc * 79 + c)) % 16777216; };

        Map<Integer, Integer> hashes = new HashMap<>();
        for (String word : words) {
            int hash = hashFunc.apply(word);
            if (hash < 0 || hash >= 16777216) {
                throw new Exception("hash too long for word: " + word + " hash: " + hash);
            }

            Integer numOccurences = hashes.get(hash);
            if (numOccurences == null) {
                numOccurences = 0;
            }
            numOccurences++;

            hashes.put(hash, numOccurences);
        }

        int numCollisions = hashes.values().stream().filter(i -> i > 1).reduce(Integer::sum).get();
        System.out.println("num collisions: " + numCollisions);
    }
}

@muddyfish您可以查看当前版本吗?我认为香港专业教育学院占三向冲突,并仍然得到相同的结果。
Bewusstsein '16

这不考虑三向碰撞。如果替换hashesMap<Integer, Integer> hashes = new HashMap<>(),然后计算每个哈希的单词数,则可以正确说明它们。
彼得·泰勒

您的得分看起来仍然不正确。我认为要计算正确的分数,您必须输出numHashes + numCollisions。(我想这会让您接近我对随机甲骨文的6832估算值。)
kasperd '16

修改评分部分为:pastebin.com/nLeg4qut
TheNumberOne

是的,修复了分级,现在看起来似乎更合理了,ty
Bewusstsein '16

1

蟒蛇,6995 6862 6732

只是一个简单的RSA功能。很la脚,但击败了一些答案。

M=0x5437b3a3b1
P=0x65204c34d
def H(s):
    n=0
    for i in range(len(s)):
        n+=pow(ord(s[i]),P,M)<<i
    return n%(8**8)

1

C ++:7112 6694 6483 6479 6412 6339冲突,90个字节

我为系数数组实现了一个简单的遗传算法。我将更新此代码,因为它会找到更好的代码。:)

int h(const char*s){uint32_t t=0,p=0;while(*s)t="cJ~Z]q"[p++%6]*t+*s++;return t%16777213;}

测试功能:

int main(void)
{
    std::map<int, int> shared;

    std::string s;
    while (std::cin >> s) {
        shared[h(s.c_str())]++;
    }

    int count = 0;
    for (auto c : shared) {
        if ((c.first & 0xFFFFFF) != c.first) { std::cerr << "invalid hash: " << c.first << std::endl; }
        if (c.second > 1) { count += c.second; }
    }

    std::cout << count << std::endl;
    return 0;
}

1

C#,6251 6335

int H(String s){int h = 733;foreach (char c in s){h = (h * 533 + c);}return h & 0xFFFFFF;}

常数533和733 889和155在到目前为止我搜索过的所有参数中给出了最好的分数。


1

tcl

88字节,6448/3233次冲突

我看到人们一直在计算冲突单词的数量,或者非空存储桶中放置的单词的数量。我给出了两个数字-第一个是根据问题的具体说明,第二个是更多的海报正在报告的内容。

# 88 bytes, 6448 collisions, 3233 words in nonempty buckets

puts "[string length {proc H w {incr h;lmap c [split $w {}] {set h [expr (2551*$h+[scan $c %c])%2**24]};set h}}] bytes"

proc H w {incr h;lmap c [split $w {}] {set h [expr (2551*$h+[scan $c %c])%2**24]};set h}

# change 2551 above to:
#   7: 85 bytes, 25839 colliding words, 13876 words in nonempty buckets
#   97: 86 bytes, 6541 colliding words, 3283 words in nonempty buckets
#   829: 87 bytes, 6471 colliding words, 3251 words in nonempty buckets


# validation program

set f [open ~/Downloads/british-english-huge.txt r]
set words [split [read $f] \n]
close $f

set have {};                        # dictionary whose keys are hash codes seen
foreach w $words {
    if {$w eq {}} continue
    set h [H $w]
    dict incr have $h
}
set coll 0
dict for {- count} $have {
    if {$count > 1} {
        incr coll $count
    }
}
puts "found $coll collisions"

2
您在哪里看到使用不正确的方法计算分数的答案?有很多东西,但是都在几年前被更正或删除。我看到剩下的四个答案得分都低于6000,因为这四个答案实际上已经过优化以具有如此低的分数。
kasperd

1
据我所知,您的代码是proc H w {incr h;lmap c [split $w {}] {set h [expr (2551*$h+[scan $c %c])%2**24]};set h}...正确吗?
暴民埃里克(Erik the Outgolfer)

@EriktheOutgolfer:是的,是
sergiol

1
我第二个@kasperd:根据问题规范,您能指出哪些答案不能解释碰撞吗?您真的尝试过运行它们吗?
sergiol

1

Python 3,89个字节,6534个哈希冲突

def H(x):
 v=846811
 for y in x:
  v=(972023*v+330032^ord(y))%2**24
 return v%2**24

您在此处看到的所有大幻数都是软糖常数。


1

JavaScript,121字节,3268 3250 3244 6354(3185)冲突

s=>{v=i=0;[...s].map(z=>{v=((((v*13)+(s.length-i)*7809064+i*380886)/2)^(z.charCodeAt(0)*266324))&16777215;i++});return v}

参数(13,7809064,380886,2,266324)是反复试验的。

我认为仍然可以优化,并且仍有增加额外参数的空间,可以进行进一步的优化...

验证

hashlist = [];
conflictlist = [];
for (x = 0; x < britain.length; x++) {
    hash = h(britain[x]);                      //britain is the 340725-entry array
    hashlist.push(hash);
}

conflict = 0; now_result = -1;
(sortedlist = sort(hashlist)).map(v => {
    if (v == now_result) {
        conflict++;
        conflictlist.push(v);
    }
    else
        now_result = v;
});

console.log(conflictlist);

var k = 0;
while (k < conflictlist.length) {
    if (k < conflictlist.length - 1 && conflictlist[k] == conflictlist[k+1])
        conflictlist.splice(k,1);
    else
        k++;
}

console.log(conflict + " " + (conflict+conflictlist.length));

3268> 3250-将第三个参数从380713更改为380560。

3250> 3244-将第三个参数从380560更改为380886。

3244> 6354-将第二个参数从7809143更改为7809064,发现我使用了错误的计算方法; P


1

这里有一些类似的构造,它们是“可植入的”并且使增量参数优化成为可能。该死的要低于6k很难!假设分数的平均值为6829,标准差为118,我还计算了随机获得如此低分数的可能性。

Clojure A,6019,Pr = 1:299.5e9

 #(reduce(fn[r i](mod(+(* r 811)i)16777213))(map *(cycle(map int"~:XrBaXYOt3'tH-x^W?-5r:c+l*#*-dtR7WYxr(CZ,R6J7=~vk"))(map int %)))

Clojure B,6021,Pr = 1:266.0e9

#(reduce(fn[r i](mod(+(* r 263)i)16777213))(map *(cycle(map int"i@%(J|IXt3&R5K'XOoa+Qk})w<!w[|3MJyZ!=HGzowQlN"))(map int %)(rest(range))))

Clojure C,6148,Pr = 1:254.0e6

#(reduce(fn[r i](mod(+(* r 23)i)16777213))(map *(cycle(map int"ZtabAR%H|-KrykQn{]u9f:F}v#OI^so3$x54z2&gwX<S~"))(for[c %](bit-xor(int c)3))))

Clojure,6431,Pr = 1:2.69e3(有些不同)

#(mod(reduce bit-xor(map(fn[i[a b c]](bit-shift-left(* a b)(mod(+ i b c)19)))(range)(partition 3 1(map int(str"w"%"m")))))16776869)

这是我最初的临时哈希函数,它具有四个可调参数。


降低分数的诀窍是使用字符串常量,在其中可以独立优化每个字符,而不会破坏您对其他字符所做的优化。
kasperd

是的,我尝试首先仅针对较短的字符串进行优化,因为将更多字符附加到“熵”字符串不会影响那些字符(一旦乘数r固定)。但是我的搜索算法本质上还是蛮力的,我不确定乘数的初始选择r是否重要。
NikoNyrh

也许仅将ASCII值相乘不会给游戏带来足够的熵。许多计分算法似乎具有形式f(n) % (8^8 - g(n))
NikoNyrh

有一个答案可以解释它如何低至3677。那些得分甚至更低的答案几乎没有解释。
kasperd

0

Ruby,6473次冲突,129个字节

h=->(w){@p=@p||(2..999).select{|i|(2..i**0.5).select{|j|i%j==0}==[]};c=w.chars.reduce(1){|a,s|(a*@p[s.ord%92]+179)%((1<<24)-3)}}

@p变量由999以下的所有素数填充。

这会将ascii值转换为素数,并将其乘积取大素数。179的忽悠系数处理的事实是,原始算法用于查找字谜,其中所有由相同字母重排的单词都具有相同的哈希值。通过在循环中添加因子,可以使字谜具有不同的代码。

我可以删除** 0.5(针对素数的sqrt测试),但代价是性能较差,以缩短代码。我什至可以使素数查找器在循环中执行,以删除另外9个字符,剩下115个字节。

为了进行测试,以下尝试在1到300的范围内找到软糖系数的最佳值。假定/ tmp目录中的word文件:

h=->(w,y){
  @p=@p||(2..999).
    select{|i|(2..i**0.5). 
    select{|j|i%j==0}==[]};
  c=w.chars.reduce(1){|a,s|(a*@p[s.ord%92]+y)%((1<<24)-3)}
}

american_dictionary = "/usr/share/dict/words"
british_dictionary = "/tmp/british-english-huge.txt"
words = (IO.readlines british_dictionary).map{|word| word.chomp}.uniq
wordcount = words.size

fewest_collisions = 9999
(1..300).each do |y|
  whash = Hash.new(0)
  words.each do |w|
    code=h.call(w,y)
    whash[code] += 1
  end
  hashcount = whash.size
  collisions = whash.values.select{|count| count > 1}.inject(:+)
  if (collisions < fewest_collisions)
    puts "y = #{y}. #{collisions} Collisions. #{wordcount} Unique words. #{hashcount} Unique hash values"
    fewest_collisions = collisions
  end
end

1
分数看起来可疑。您确定要计算所有冲突的单词吗?先前的几个答案错误地为每个冲突的哈希值仅计算了一个单词。
kasperd '16

或许你是对的。我必须考虑我的计算方式,看看它是否与您的定义相同。我正在计算有多少个单词,并减去生成了多少个唯一的哈希码。如果单词A和B获得相同的哈希码,那是一个还是两个冲突?我算是一个。
Paul Chernoch '16

1
我没有定义评分功能。我只是从发布挑战的同一用户发布的示例答案中复制了它。大多数答案的分数在6273到6848之间。存在多个答案,每个答案在分数计算中都会犯相同的错误,从而导致计算的分数大约应该是原来的一半。(如果没有三个相撞词的情况,则正确分数的一半。)
kasperd '16

1
是的,我犯了同样的错误。稍后我将修改答案。上车了。
Paul Chernoch '16

修正了得分。
Paul Chernoch '16

0

tcl

#91字节,发生6508次冲突

91字节,6502次冲突

proc H s {lmap c [split $s ""] {incr h [expr [scan $c %c]*875**[incr i]]};expr $h&0xFFFFFF}

计算机仍在执行搜索,以评估是否有引起碰撞的值小于记录者147 875的碰撞值。

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.