ツイート可能なハッシュ関数チャレンジ


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?なに…?
ルイスメンドー

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シグマイベントです。
-xnor

回答:


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、衝突する単語の数、v、最大でxの衝突する単語を取得するために必要なバイト単位のエントロピーのプロットです。たとえば、ポイント(2835、140)は、ランダム関数が確率1/256 ** 140で最大2835の衝突する単語を取得することを示しているため、140を使用した場合よりもはるかに優れた結果が得られることはほとんどありませんコードのバイト。

グラフ


理論的な限界の素晴らしい分析。その理論的な限界を克服するためには、おそらく問題の辞書に最適化された組み込み関数を備えた言語を使用する必要があります(これは不正です)。言語に暗号化ハッシュが組み込まれている場合、最適なソリューションを見つけるための制限は、多少なりとも建設的な方法に変えることができます。これを考慮してください:$ h(w || c)%2 ^ {24} $ここで、$ c $はバイト文字列定数です。ランダムオラクルモデルでは、高い確率で最適に近づくことが示されました。もちろん、$ c $を強引に強制することは不可能です。
カスペルド

グラフの式はどのように計算しましたか?本当に面白いです!
ニコニール

@NikoNyrh動的プログラミング。(wch)がw個の単語を持つ状態を表し、そのうちc個h個の個別のハッシュと衝突し、残りのwcがすべて個別のハッシュを持つとする。我々は、ランダムな単語を追加した場合、状態となる(W + 1、CH確率1) - (時間 + 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であり、CHを有する)のC ≤のX
アンダースカセオルグ

ハッシュ関数をどうやって思いついたのか誰も聞いていないなんて信じられませんか?知りたいと思っています。
アヌーシュ

22

Python、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バイトを節約します
デニス

3
@Dennis文字列定数を5バイト長くするために5バイトを使用できました。ただし、長さを変更する場合は、文字列定数を最初から作成し直す必要があります。そして、これらの5バイトが文字列の作成をやり直す価値があるほど十分な改善をもたらすかどうかはわかりません。文字列定数を最適化するためにCPU時間を既に費やしています。
カスペルド

@Dennisいくつかの余分なバイトは、エスケープを必要とする定数でいくつかの文字を使用する自由を与えると思います。そうすれば、文字列を何度も作成しなくても、いくつかの余分なバイトを使用できる可能性があります。
カスペルド

7
別のバイトが必要な場合は、2**24 == 8**8
アンダースカセオルグ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私は戦いなしでgivingめません。しかし、現在のソリューションを最適化するのをやめて、別のアプローチを見つける必要があると思います。パラメーターを最適化するために現在のアプローチを使用し続けてもあなたに勝るものはないからです。あなたのソリューションを破ったら、ソリューションを編集して戻ってきます
。...-kasperd

@kasperd:素晴らしい。:-P
アンデルスカセオルグ16

1
@AndersKaseorgどのようにして文字列を見つけますか?
ASCIIのみ

@AndersKaseorgパラメータ検索を大幅に高速化できました。そして、検索が次善のソリューションにとどまる原因となった障害を取り除きました。しかし、それでも4885を超えることはできませんでした。なぜそれ以上進められなかったのかを熟考した後、ソリューションの何が問題で、どのように修正できるかを突然知りました。今、あなたのソリューションのアフィン変換は私にとって完全に理にかなっています。追いつくことができる唯一の方法は、アフィン変換を自分で使うことだと思います。
カスペルド

1
@kasperd:とてもいい。n%(8**8-ord('…'[n%70]))他のパラメーターを変更せずに、より良い文字列を検索すると、4995にしか到達できなかったため、新しいオプティマイザーが私のものに追いついたようです。今、これはもっと面白くなります!
アンデルスカセオルグ16

16

CJam、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に移植しました。
デニス

hPythonのポートはCJamの組み込みに対応することで?
カスペルド

はい。それはCJamのb(基本変換)です。
デニス

採点プロセスはbashで行われていますか?
GamrCorps 16

@GamrCorpsはい、それはBashです。
デニス

11

Python、6446 6372


このソリューションは、以前のすべてのエントリよりも少ない衝突カウントを実現し、コードに許可されている140バイトのうち44バイトのみを必要とします。

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

2
@ mbomb007 orlp自身の提出は%(2**24-1)そうですので、明確化を求めるのは良いかもしれません
-Sp3000

12
@ mbomb007チャレンジはそのようなことを言っていません。関数は入力としてASCII文字列を取り、その範囲の整数を出力する必要があると述べています。どの入力を関数に与えても、出力はその範囲になります。ワード関数の数学的な定義では、許可されたすべての出力を生成する必要はありません。もしそれがあなたが望んでいるのであれば、使用する数学用語は全射関数でした。ただし、要件に「単射」という言葉は使用されていません。
カスペルド

@ mbomb007:ハッシュ関数が全射であるという要件はありません。たとえば、多くのメモリアドレスベースのハッシュ関数は、古いバージョンのPythonのデフォルトのオブジェクトハッシュなど、メモリのアライメントにより、2の小さいべき乗の倍数しか生成できません。多くのハッシュ関数は、コドメインよりも小さなドメインを持っているため、とにかく全射ではありません。
user2357112

3
@ mbomb007 -実際には、ある与えられたはるかに多くの数の値から[0, 2**24-1]単語が英語であるよりも、数学的になることは不可能、その範囲内のすべての単一の値が可能であったハッシュを作ること。
ダレルホフマン

7

CJam、6273

{49f^245b16777213%}

各文字を49とXORし、結果の文字列をx、y 245 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でアルゴリズムを再実装しました。公式のスコア計算でスコアがチェックアウトされることを確認できます。
カスペルド

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、ハッシュ関数として定義されています。

ハッシュ関数から9バイトを削ってくれたNeilに感謝します!


なぜ割り当てますか?また、代わりに((...)>>>0)%(1<<24)を使用することもできます(...)<<8>>>8
ニール

@Neilアルファベットのため、そして私は退屈だ= Pまた、素敵なビット単位の計算!7バイトの節約=)
Mwr247

良いことは、これはコードゴルフではありませんi
ニール

@Neil Crap> _ <いくつかの代替ハッシュアイデアをテストしているときにXDを削除するのを忘れていました同じ140バイトに、すべてのビットが役立ちます;)
Mwr247

1
@ Sp3000 Gah、おっしゃるとおりです。私は衝突が見つかったときに最初にそこにあるものを数えていません。それを修正します。
Mwr247

5

Mathematica、6473

次のステップ...文字コードを合計する代わりに、それらを2 24を法とする前に、それらをbase-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);

プログラミングパズルとコードゴルフへようこそ!
アレックスA.

...そして、@ AlexAの暖かい歓迎に感謝します!
binarymax

5

Python、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
スコア関数に、ハッシュ関数からの戻り値が許容範囲内の整数であることをアサートさせると便利です。
カスペルド

4

Python、6390 6376 6359

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

MartinBüttnerの答えに対する些細な修正と考えられるかもしれません。


3
@ mbomb007それは真実ではありません。関数が常に4を出力する場合、範囲内でまだ出力しています[0, 2**24-1]。許可されていない唯一のものは、その範囲内にない数値を出力することです(例:-1または)2**24
-orlp


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明示的にcharに変換する必要はありません。また、を使用するnumelこともできますlength。ただし、これらすべての余分なバイトをどうするかはわかりません!
-Suever

1

ルビー、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個の位置に割り当ててから、すべてのprime [i] **(string [i]のASCIIコード)を合計し、次にmod 2 ** 24-1を合計します。


1

Javaの8、7054 6467

これは組み込みのjava.lang.String.hashCode関数からインスピレーションを受けています(コピーされていません)。

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は現在のバージョンを確認できますか?私はiveが3-way-collisionsの原因であり、まだ同じ結果を得ていると思います。
ベウストセイン

これは、3方向の衝突を考慮していません。に置き換えhashesMap<Integer, Integer> hashes = new HashMap<>()、各ハッシュの単語数を数えると、それらを正しく説明できます。
ピーターテイラー

あなたのスコアはまだ正しく見えません。正しいスコアを計算するには、numHashes + numCollisionsを出力する必要があります。(ランダムオラクルの6832推定に近いと思います。)
kasperd

これにグレーディング部分を変更します。pastebin.com/nLeg4qut
TheNumberOne

ええ、グレーディングを修正し、今でははるかに合理的な値になっているように見えます、ty
Bewusstsein

1

Python、6995 6862 6732

単純なRSA関数です。かなり不自由ですが、いくつかの答えを打ち負かしています。

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コリジョン

衝突する単語の数、または空でないバケツに入れられた単語の数のいずれかを人々が数えているのがわかります。私は両方のカウントを与えます-最初は問題の仕様によるもので、2番目はより多くのポスターが報告しているものです。

# 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
スコアの計算に不適切な方法を使用した回答はどこにありますか?たくさんありましたが、それらはすべて数年前に修正または削除されました。これらの4つの回答は実際にそのような低いスコアを持つように最適化されているため、6000未満のスコアで4つの回答が残っています。
カスペルド

1
私が知る限り、あなたのコードはproc H w {incr h;lmap c [split $w {}] {set h [expr (2551*$h+[scan $c %c])%2**24]};set h}...正しいですか?
エリックアウトゴルファー

@EriktheOutgolfer:はい、そうです
-sergiol

1
@kasperdを2回目にします。質問の仕様に従って、どの答えが衝突を考慮していないかを指摘できますか。あなたは本当にそれらを実行しようとしましたか?
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-3番目のパラメーターを380713から380560に変更しました。

3250> 3244-3番目のパラメーターを380560から380886に変更しました。

3244> 6354-2番目のパラメーターを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)

これは私のオリジナルのアドホックハッシュ関数で、4つの調整可能なパラメーターがあります。


低スコアの秘Theは、他の文字に対して行った最適化を損なうことなく、各文字を個別に最適化できる文字列定数です。
カスペルド

ええ、「エントロピー」文字列により多くの文字を追加してもそれらに影響しないので、最初に短い文字列のみを最適化しようとしました(乗数rが修正されたら)。しかし、それでも私の検索アルゴリズムは本質的にブルートフォースであり、乗数の最初の選択rが重要かどうかはわかりません。
ニコニール

ASCII値を乗算するだけでは、ゲームに十分なエントロピーがもたらされない可能性があります。多くの得点の高いアルゴリズムの形式はのようf(n) % (8^8 - g(n))です。
ニコニール

3677という低い値になった理由を説明する答えが1つあります。それよりも低いスコアを付けたものにはほとんど説明がありません。
カスペルド

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(primeの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
スコアは疑わしいようです。衝突するすべての単語を数えていますか?以前のいくつかの回答では、衝突するハッシュ値ごとに誤って1ワードしかカウントされませんでした。
カスペルド

君の言う通りかもね。カウント方法を検討し、定義と同じかどうかを確認する必要があります。単語の数をカウントし、生成された一意のハッシュコードの数を減算しています。単語AとBが同じハッシュコードを取得する場合、その1つまたは2つの衝突ですか?私は1つとして数えます。
ポールチェルノック

1
スコアリング関数を定義しませんでした。チャレンジを投稿した同じユーザーが投稿した回答例からコピーしました。ほとんどの回答のスコアは6273から6848の範囲です。スコアの計算でそれぞれ同じ間違いをする複数の回答があり、本来の約半分のスコアを計算します。(3つの単語が衝突するケースがない場合は、正確なスコアのちょうど半分。)
kasperd

1
はい、同じ間違いをしました。後で答えを修正します。バスに乗ろうとしました。
ポールチェルノック

得点を修正しました。
ポールチェルノック

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ベース(まだ記録担当者)よりも少ない衝突を引き起こす値があるかどうかを評価するために、まだ検索を実行しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.