Завдання функції хеш-твітів


73

У цьому ви напишете хеш-функцію в 140 байт 1 або менше вихідного коду. Хеш-функція повинна приймати рядок ASCII і вводити 24-бітне ціле число без підпису ([0, 2 24 -1]) як вихід.

Ваша хеш-функція буде оцінена для кожного слова у цьому великому англійському англійському словнику 2 . Ваш бал - це кількість слів, які поділяють хеш-значення з іншим словом (зіткнення).

Виграє найнижчий бал, зв’язки розірвані першим плакатом.

Тестовий випадок

Перш ніж надсилати, будь ласка, протестуйте свій сценарій оцінювання на наступному вкладі:

duplicate
duplicate
duplicate
duplicate

Якщо вона дає будь-яку оцінку, крім 4, це баггі.


Уточнюючі правила:

  1. Ваша хеш-функція повинна працювати на одній строці, а не на цілому масиві. Крім того, ваша хеш-функція може не виконувати жодного іншого вводу-виводу, крім вхідного рядка та цілого виводу.
  2. Вбудовані хеш-функції або подібні функціональні можливості (наприклад, шифрування до байтів для сканування) заборонено.
  3. Ваша хеш-функція повинна бути детермінованою.
  4. На відміну від більшості інших змагань, дозволено оптимізувати спеціально для введення рахунків.

1 Мені відомо, що Twitter обмежує символи замість байтів, але для простоти ми будемо використовувати байти як обмеження для цього завдання.
2 Модифікований з величезного вбрання Debian , видаляючи будь-які слова, що не належать до ASCII.


11
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch's? Що за...?
Луїс Мендо

8
@DonMuesli en.wikipedia.org/wiki/Llanfairpwllgwyngyll (цікавий факт: це слово також є у вбудованому словнику стиснення Jelly)
Мартін Ендер

8
Я думаю, вам слід заборонити вбудовані словники.
Денніс

4
Для довідки: Взявши 24 MSB SHA-512, ви отримаєте результат 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, кількість скупчуваних слів, порівняно з y, ентропія в байтах, необхідна для отримання не більше x зіштовхуваних слів. Наприклад, точка (2835, 140) повідомляє нам, що випадкова функція отримує щонайменше 2835 стикаються слів із ймовірністю 1/256 ** 140, тож це малоймовірно, що ми коли-небудь зможемо зробити набагато краще, ніж це зі 140 байтів коду.

графік


Хороший аналіз теоретичних меж. Для того, щоб подолати цю теоретичну межу, можливо, доведеться використовувати мову зі вбудованими функціями, оптимізованими для словника у питанні (що було б обманом). Якщо мова має вбудований криптографічний хеш, ліміт може бути перетворений на більш-менш конструктивний метод пошуку оптимального рішення. Розглянемо це: $ h (w || c)% 2 ^ {24} $ де $ c $ - константа рядка байтів. У випадковій моделі оракул, яку можна було б наблизити до оптимальної з високою ймовірністю. Звичайно, жорстоке примушування $ c $ було б недоцільним.
kasperd

Як ви обчислили формулу для графіка? Дійсно цікаво!
NikoNyrh

@NikoNyrh Динамічне програмування. Нехай ( w , c , h ) являє собою стан зі словами w , з яких c стикаються з h різними хешами, а решта w - c мають усі чіткі хеші. Якщо додати випадкове слово, стан стає ( w + 1, c , h ) з ймовірністю 1 - ( h + w - c ) / 2 ^ 24, або ( w + 1, c + 1, h ) з ймовірністю h / 2 ^ 24, або ( w + 1, c+ 2, h + 1) з ймовірністю ( w - c ) / 2 ^ 24. Тоді кінцева ентропія, зібрана з x зіштовхуються слів, є базою журналу 1/256 суми ймовірностей у станах (340275, c , h ) з cx .
Anders Kaseorg

Не можу повірити, що ніхто не запитав, як ви придумали хеш-функцію? Мені б дуже цікаво знати.
Ануш

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 байт, якщо ви їх зможете якось використати ...
Денніс

3
@Dennis Я міг би використати 5 байт, щоб збільшити рядок постійним на 5 байт. Але мені доведеться починати з побудови константи струни з нуля, якщо я зміню довжину. І я не впевнений, що ці 5 байт дадуть мені вдосконалення, що варто почати над створенням рядка. Я вже витрачав години на процесор, оптимізуючи постійну константу.
kasperd

@Dennis Я думаю, що кілька зайвих байтів дали б мені свободу використовувати деякі символи в постійній необхідності втечі. Таким чином, я міг би потенційно використовувати кілька зайвих байтів, не будуючи знову створювати рядок.
kasperd

7
Якщо ви хочете ще один байт, 2**24 == 8**8.
Андерс Касеорг

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

1
@AndersKaseorg Як знайти рядок?
Лише ASCII

@AndersKaseorg Мені вдалося значно прискорити пошук параметрів. І я усунув перешкоду, через яку мій пошук зациклювався на неоптимальних рішеннях. Але я все ще не міг зробити це кращим, ніж 4885. Після деякого роздуму над тим, чому я не можу зробити це далі, я раптом зрозумів, що не так у моєму рішенні та як це можна виправити. Тепер афінна трансформація у вашому рішенні має для мене ідеальний сенс. Я думаю, що єдиний спосіб, який я можу наздогнати, - це застосувати афінну трансформацію.
kasperd

1
@kasperd: Дуже приємно. Під час пошуку кращого рядка n%(8**8-ord('…'[n%70]))без інших змін параметрів мені вдалося отримати лише 4995, тож схоже, що ваш новий оптимізатор наздогнав мою. Тепер це стає цікавішим!
Андерс Касеорг

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 для легкої перевірки.
Денніс

Чи відповідає hцей порт Python вбудованому CJam?
kasperd

Так. Це CJam b(базове перетворення).
Денніс

Ваш бал в баш?
GamrCorps

@GamrCorps Так, це Bash.
Денніс

11

Пітон, 6446 6372


Це рішення досягає меншої кількості зіткнень, ніж усі попередні записи, і йому потрібно лише 44 із 140 байтів, дозволених для коду:

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

2
@ mbomb007 власне подання orlp %(2**24-1)так, я думаю, що було б корисно попросити роз'яснення
Sp3000

12
@ mbomb007 Проблема говорить, що такого немає. Він говорить, що функція повинна приймати рядок ASCII як вхід і виводити ціле число в цьому діапазоні. Незалежно від того, на якому введенні ви надаєте мою функцію, вихід буде знаходитися в такому діапазоні. Математичне визначення функції слова не вимагає, щоб вона створювала кожен дозволений результат. Якщо це те, що ви хотіли, щоб математичний термін ви використовували, - це суржективна функція. Але слово surjective в вимогах не вживалося.
kasperd

@ mbomb007: Немає вимоги, щоб хеш-функції були сур'єктивними. Наприклад, багато хеш-функцій на основі пам'яті-адреси можуть створювати кратні з невеликою потужністю 2 за рахунок вирівнювання пам'яті, включаючи хеш об'єктів за замовчуванням у старих версіях Python. Багато хеш-функцій навіть мають менший домен, ніж кодомейн, так що вони ніяк не можуть бути сюрєктивними.
user2357112

3
@ mbomb007 - Насправді, враховуючи, що в англійській мові є набагато більше числових значень, [0, 2**24-1]ніж в англійській мові, математично неможливо зробити хеш, де було б можливо кожне значення в цьому діапазоні.
Даррел Гофман

7

CJam, 6273

{49f^245b16777213%}

XOR кожного символу на 49 , зменшіть отриманий рядок через x, y ↦ 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

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)

Виклик як node hash.js dictionary.txt, де hash.jsє сценарій, dictionary.txtє текстовим файлом словника (без остаточного нового рядка) і Fвизначається як хеш-функція.

Дякую Нілу за гоління 9 байт за функцію хешування!


Чому присвоєння а? Також замість ((...)>>>0)%(1<<24)вас, ймовірно, можна використовувати (...)<<8>>>8.
Ніл

@Neil Тому, що алфавіт, і мені нудно = P Також приємне побітне математика! Це врятувало 7 байт =)
Mwr247,

Хороша річ, що це не код гольфу, інакше мені доведеться також відмовити вас за невикористану змінну i.
Ніл

@Neil Crap> _ <Я використовую це під час тестування деяких альтернативних хешируючих ідей і забув видалити XD Так, добре, що це не гольф, хоча все-таки я хотів би це зробити, якщо я міг би стиснути хеш-функції та рахунки. в ті ж 140 байт, тому кожен біт допомагає;)
Mwr247

1
@ Sp3000 Gah, я бачу, що ти маєш на увазі. Моє не враховує тих, які є там спочатку, коли зіткнення знайдено. Я це виправлю.
Mwr247

5

Математика, 6473

Наступний крок ... замість того, щоб підсумовувати символьні коди, ми розглядаємо їх як цифри номера базового-151, перш ніж приймати їх за модулем 2 24 .

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

Ласкаво просимо до головоломки програмування та коду для гольфу!
Олексій А.

... І дякую за теплий прийом @AlexA.!
binarymax

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

4

Пітон, 6390 6376 6359

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

Може вважатись тривіальною зміною відповіді Мартіна Бюттнера .


3
@ mbomb007 Це неправда. Якщо ваша функція завжди виводить 4, вона все одно виводиться в діапазон [0, 2**24-1]. Єдине, що заборонено - це вивести будь-яке число, яке не знаходиться в цьому діапазоні, наприклад, -1або 2**24.
orlp


2

Матлаб, 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

Якщо ви хочете заощадити місце у вашій програмі, вам не доведеться doubleявно перетворювати свою таблицю в явну. Також ви можете використовувати, 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 позицій рядка, потім підсумовуйте всі прості [i] ** (код ascii рядка [i]), потім mod 2 ** 24-1.


1

Ява 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 Ви можете перевірити поточну версію? я думаю, що я припадав на 3-х колісні зіткнення, і я все одно отримую той же результат.
Bewusstsein

Це не враховує тристоронніх зіткнень. Якщо замінити hashesз , Map<Integer, Integer> hashes = new HashMap<>()а потім підрахувати кількість слів для кожного хеша, ви можете пояснити їх правильно.
Пітер Тейлор

Ваша оцінка все ще виглядає неправильно. Я думаю, щоб обчислити правильний результат, ви повинні вивести numHashes + numCollisions. (Що, напевно, дозволить вам наблизитись до моєї оцінки 6832 щодо випадкового оракула.)
kasperd,

Змініть частини класифікації на це: pastebin.com/nLeg4qut
TheNumberOne

так, виправлено оцінку, і зараз це виглядає як набагато більш розумне значення, ти
Bewusstsein

1

Пітона, 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

ткл

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}... так?
Ерік Аутгольфер

@EriktheOutgolfer: Так, це
sergiol

1
Я другий @kasperd: Чи можете ви вказати, що відповіді не враховують зіткнення відповідно до специфікації запитань? Ви справді намагалися запустити їх?
серхіол

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

Ось декілька подібних конструкцій, які є досить «затравочними» та робить можливою інкрементальну оптимізацію параметрів. Чорт важко отримати нижче 6 к! Якщо припустити, що середній бал становить 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

Рубі, 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 для прайму) за рахунок поганої продуктивності, щоб скоротити код. Я навіть міг змусити пошук просте число виконувати в циклі, щоб видалити ще дев'ять символів, залишивши 115 байт.

Щоб перевірити, наступне намагається знайти найкраще значення для коефіцієнта викривлення в діапазоні від 1 до 300. Передбачається, що файл слова в каталозі / tmp:

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

Можливо ти правий. Я повинен розглянути, як я порахував і побачити, чи це те саме, що ваше визначення. Я підраховую, скільки слів існує і віднімаю, скільки унікальних хеш-кодів було сформовано. Якщо слова A і B отримують однаковий хеш-код, це одне зіткнення чи два? Я рахую це як одне.
Павло Чорноч

1
Я не визначив функцію оцінювання. Я просто скопіював це з прикладу відповіді, опублікованого тим самим користувачем, який опублікував виклик. Більшість відповідей мають бали від 6273 до 6848. У підрахунку балів було декілька відповідей, кожен з яких робив одну і ту ж помилку, що призводило до обчислення балів приблизно в половині того, що повинно було бути. (Рівно половина правильної оцінки, якщо немає випадків трьох стикаються слів.)
kasperd

1
Так, я зробив ту саму помилку. Свою відповідь я поправлю пізніше. Треба спіймати автобус.
Павло Чорноч

Зафіксована оцінка.
Пол Черноч

0

ткл

# 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, яка все ще є рекордсменом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.