트윗 가능한 해시 함수 도전


73

에서는 140 바이트 1 이하의 소스 코드 로 해시 함수를 작성합니다. 해시 함수는 ASCII 문자열을 입력으로 사용하고 24 비트 부호없는 정수 ([0, 2 24 -1])를 출력으로 반환해야합니다 .

이 큰 영국 영어 사전 2의 모든 단어 대해 해시 함수가 평가됩니다 . 점수는 다른 단어 (충돌)와 해시 값을 공유하는 단어의 양입니다.

가장 낮은 점수가 이기고 첫 번째 포스터에 의해 결속됩니다.

테스트 사례

제출하기 전에 다음 입력에서 채점 스크립트를 테스트하십시오.

duplicate
duplicate
duplicate
duplicate

4 이외의 점수를 주면 버그가 있습니다.


명확화 규칙 :

  1. 해시 함수는 전체 배열이 아닌 단일 문자열에서 실행해야합니다. 또한 해시 함수는 입력 문자열 및 출력 정수 이외의 다른 I / O를 수행하지 않을 수 있습니다.
  2. 내장 해시 함수 또는 이와 유사한 기능 (예 : 스크램블 바이트 암호화)은 허용되지 않습니다.
  3. 해시 함수는 결정적이어야합니다.
  4. 스코어링 입력을 위해 특별히 최적화 된 대부분의 다른 컨테스트와 달리 허용됩니다.

1 Twitter가 바이트 대신 문자를 제한한다는 것을 알고 있지만 단순성을 위해이 도전에 대한 제한으로 바이트를 사용합니다.
2 비 ASCII 단어를 제거하여 Debian의 wbritish-huge 에서 수정되었습니다 .


11
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch's? 뭐야 ...?
Luis Mendo

8
@DonMuesli en.wikipedia.org/wiki/Llanfairpwllgwyngyll (재미있는 사실 : 그 단어는 젤리의 내장 압축 사전에도 있습니다)
Martin Ender

8
내장 사전을 허용하지 않아야한다고 생각합니다.
Dennis

4
참고 : 24 MSB의 SHA-512를 획득하면 6816의 점수를 얻습니다.
Dennis

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*Nstdin에서 줄 바꿈으로 구분 된 단어 목록을 가져와 stdout에서 줄 바꿈으로 구분 된 해시 목록을 작성하도록 추가 할 수 있습니다 . 동등한 파이썬 코드 :

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.zstdin에서 줄 바꿈으로 구분 된 단어 목록을 가져와 stdout에서 줄 바꿈으로 구분 된 해시 목록을 작성하도록 추가 할 수 있습니다 . 동등한 파이썬 코드 :

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 충돌 단어를 얻는 데 필요한 바이트 단위 엔트로피 x 대 충돌 단어 수 x의 플롯입니다. 예를 들어 점 (2835, 140)은 임의 함수가 확률이 1 / 256 ** 140 인 최대 2835 개의 충돌하는 단어를 얻으므로 140을 사용하는 것보다 훨씬 더 잘 수행 할 수 없을 것입니다. 코드의 바이트.

그래프


이론적 한계에 대한 훌륭한 분석. 이론적 인 한계를 극복하기 위해서는 아마도 사전에 최적화 된 내장 함수를 가진 언어를 사용해야 할 것입니다. 언어에 내장 암호화 해시가있는 경우 제한을 최적의 솔루션을 찾기위한 다소 건설적인 방법으로 바꿀 수 있습니다. $ h (w || c) % 2 ^ {24} $ 여기서 $ c $는 바이트 문자열 상수입니다. 확률이 높을수록 최적에 가까워지는 랜덤 오라클 모델. 물론 $ c $를 강요하는 것은 불가능할 것입니다.
kasperd

그래프의 수식은 어떻게 계산 했습니까? 정말 흥미로운!
NikoNyrh

@NikoNyrh 동적 프로그래밍. ( w , c , h )가 w 단어가 있는 상태를 나타내도록하고 , 그 중 ch 개의 고유 해시 와 충돌 하고 나머지 w - c는 모두 고유 한 해시를 갖습니다. 우리는 임의의 단어를 추가하는 경우, 상태가된다 ( w + 1, C , H 확률 1) - ( 시간 + w - C / 2 ^ (24) 또는 () w + 1, C + 1, H 확률)와 H / 2 ^ 24 또는 ( w + 1, c+ 2, h + 1) 확률 ( w - c ) / 2 ^ 24 그런 다음 x 충돌 단어로 그래프로 표시되는 최종 엔트로피 는 cx 인 상태 (340275, c , h ) 에서 확률 합계의 로그베이스 1/256입니다 .
Anders Kaseorg

아무도 해시 함수를 어떻게 만들 었는지 믿을 수 없습니까? 알고 싶습니다.
Anush

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

@ 데니스 나는 여분의 바이트가 필요하기 때문에 일정한 이스케이프가 필요한 일부 문자를 자유롭게 사용할 수 있다고 생각합니다. 그렇게하면 문자열을 다시 구성하지 않고도 몇 가지 추가 바이트를 사용할 수 있습니다.
kasperd

7
다른 바이트를 원하면 2**24 == 8**8.
Anders Kaseorg

20

Python 2, 140 바이트, 4266 충돌 단어

명확하지 않은 트윗 가능성으로 인해 인쇄 할 수없는 바이트로 시작하고 싶지는 않았지만 시작하지 않았습니다. :-피

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
앤더스 Kaseorg

1
@AndersKaseorg 문자열은 어떻게 찾습니까?
ASCII 전용

@AndersKaseorg 나는 매개 변수 검색 속도를 크게 높였습니다. 그리고 검색이 차선책으로 해결되는 장애물을 제거했습니다. 그러나 나는 여전히 4885보다 더 나아질 수 없었습니다. 왜 더 이상 그것을 만들 수 없는지에 대해 깊이 생각한 후에 갑자기 내 솔루션의 문제점과 해결 방법을 깨달았습니다. 이제 솔루션의 아핀 변환이 나에게 완벽합니다. 내가 따라 잡을 수있는 유일한 방법은 아핀 변환을 사용하는 것입니다.
kasperd

1
@kasperd : 아주 좋아요. n%(8**8-ord('…'[n%70]))다른 매개 변수를 변경하지 않고 더 나은 문자열을 검색 할 때 4995에 도달했을 뿐이므로 새 옵티마이 저가 따라 잡은 것처럼 보입니다. 이제 이것은 더욱 흥미로워집니다!
Anders Kaseorg

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으로 이식했습니다.
Dennis

않는 h그 파이썬 포트에 CJam의 내장에 해당?
kasperd

예. CJam b(기본 변환)입니다.
Dennis

당신의 득점 과정은 배쉬입니까?
GamrCorps

@GamrCorps 네, Bash입니다.
Dennis

11

파이썬, 6446 6372


이 솔루션은 모든 이전 항목보다 충돌 횟수가 적으며 코드에 허용 된 140 바이트 중 44 개만 필요합니다.

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

2
@ mbomb007 orlp의 자체 제출은 %(2**24-1)그렇기 때문에 설명을 요청하는 것이 좋을 것
같습니다.

12
@ mbomb007 도전은 그런 말을하지 않습니다. 함수는 ASCII 문자열을 입력으로 사용하고 해당 범위의 정수를 출력해야한다고 말합니다. 어떤 기능을 입력하든 관계없이 출력이 해당 범위에있게됩니다. 단어 함수의 수학적 정의는 모든 허용 된 출력을 생성 할 필요는 없습니다. 그것이 당신이 원하는 수학적 용어가 원하는 것이라면 의심스러운 기능이었습니다. 그러나 요구 사항에는 추측이라는 단어가 사용되지 않았습니다.
kasperd

@ mbomb007 : 해시 함수를 추측 할 필요는 없습니다. 예를 들어, 많은 메모리 주소 기반 해시 함수는 이전 버전의 Python에서 기본 객체 해시를 포함하여 메모리 정렬로 인해 2의 작은 힘의 배수 만 생성 할 수 있습니다. 많은 해시 함수는 심지어 codomain보다 작은 도메인을 가지고 있기 때문에 어쨌든 의심 할 수 없습니다.
user2357112

3
@ mbomb007 - 사실, 거기에 주어진 멀리 에서 더 많은 숫자 값 [0, 2**24-1]영어 단어가보다, 그것을 수학적 것 없는 그 범위에있는 모든 단일 값이 가능했다 해시를 만들어가.
Darrel Hoffman

7

CJam, 6273

{49f^245b16777213%}

각 문자를 49로 XOR하고 , 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

귀하의 설명에서 파이썬의 알고리즘을 다시 구현했습니다. 공식 점수 계산을 통해 점수가 체크 아웃되었음을 확인할 수 있습니다.
kasperd

7

자바 스크립트 (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

@Neil 알파벳 때문에 지루 해요. = P 또한 비트 단위의 계산이 좋았습니다! 7 바이트 절약 =)
Mwr247

코드 골프가 아닌 것이 좋습니다. 그렇지 않으면 사용하지 않는 변수 i도 딩해야 합니다.
Neil

@Neil Crap> _ <대체 해싱 아이디어를 테스트하고 XD를 제거하는 것을 잊어 버렸습니다. 그렇습니다. 골프가 아닙니다. 해시와 스코어링 기능을 압축 할 수 있다면 그것을 좋아합니다. 같은 140 바이트로, 그래서 모든 비트가 도움이됩니다;)
Mwr247

1
@ Sp3000 Gah, 무슨 말인지 알겠습니다. 광산은 충돌이 발견되었을 때 처음에 있던 것을 세지 않습니다. 내가 고칠 게
Mwr247

5

매스 매 티카, 6473

다음 단계는 ... 문자 코드를 합산하는 대신 모듈로 2 24를 취하기 전에베이스 -151 숫자의 숫자로 취급합니다 .

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

충돌 횟수를 결정하는 간단한 스크립트는 다음과 같습니다.

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

방금 체계적으로 모든 기지를 시험해 보았 1으며 지금까지 151 기지는 가장 적은 충돌을 일으켰습니다. 점수를 조금 더 낮추기 위해 몇 가지를 더 시도하지만 테스트는 약간 느립니다.


5

자바 스크립트 (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

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)

Martin Bü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

프로그램에서 공간을 절약하려면 문자를 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] ** (문자열 [i]의 ASCII 코드)를 합한 다음 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 현재 버전을 확인할 수 있습니까? 나는 ive가 3 방향 충돌을 설명했지만 여전히 같은 결과를 얻고 있다고 생각합니다.
Bewusstsein

이것은 3 방향 충돌을 설명하지 않습니다. 당신이 교체 할 경우 hashesMap<Integer, Integer> hashes = new HashMap<>()다음 각 해시에 대한 단어의 수를 계산, 당신은 정확하게 설명 할 수.
Peter Taylor

점수가 여전히 부정확 해 보입니다. 올바른 점수를 계산하려면 numHashes + numCollisions를 출력해야합니다. (임의의 오라클에 대한 내 6832 추정치에 가깝게 생각할 것입니다.)
kasperd

채점 부분을 다음과 같이 수정하십시오. pastebin.com/nLeg4qut
TheNumberOne

그래, 등급을 수정하고 지금 훨씬 더 합리적인 가치처럼 보인다, ty
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

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 개 미만의 점수로 4 개의 답변이 남아있는 것을 볼 수 있습니다. 그 4 개의 답변은 실제로 그러한 낮은 점수를 갖도록 최적화 되었기 때문입니다.
kasperd

1
내가 알 수있는 한, 당신의 코드는 proc H w {incr h;lmap c [split $w {}] {set h [expr (2551*$h+[scan $c %c])%2**24]};set h}... 맞습니까?
Outgolfer Erik

@EriktheOutgolfer : 그렇습니다
sergiol

1
두 번째 @kasperd : 질문 사양에 따라 충돌을 설명하지 않는 답변을 지적 할 수 있습니까? 당신은 정말로 그들을 실행하려고 했습니까?
sergiol

1

파이썬 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

자바 스크립트, 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-두 번째 매개 변수를 7809143에서 7809064로 변경했는데 잘못된 계산 방법을 사용했습니다 .P


1

다음은 몇 가지 유사한 구성으로, "시드 가능"하며 증분 매개 변수 최적화가 가능합니다. 6k보다 낮아지기가 어렵다! 점수의 평균이 6829이고 표준의 118이 있다고 가정하면 이러한 낮은 점수를 무작위로 얻을 가능성을 계산했습니다.

클로저 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 %)))

클로저 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))))

클로저 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 개의 조정 가능한 매개 변수가 있습니다.


낮은 점수를 매기는 트릭은 다른 문자에 대해 수행 한 최적화를 망치지 않고 각 문자를 독립적으로 최적화 할 수있는 문자열 상수입니다.
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 테스트)를 제거하여 코드를 단축 할 수있었습니다. 루프에서 소수 찾기를 실행하여 9 개의 문자를 더 제거하고 115 바이트를 남겨 둘 수도 있습니다.

테스트하기 위해 다음은 1-300 범위의 퍼지 팩터에 가장 적합한 값을 찾으려고 시도합니다. / tmp 디렉토리에 단어 file이 있다고 가정합니다.

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가 동일한 해시 코드를 얻는다면 그 충돌은 하나입니까, 아니면 둘입니까? 나는 그것을 하나로 계산합니다.
Paul Chernoch

1
스코어링 기능을 정의하지 않았습니다. 도전을 게시 한 동일한 사용자가 게시 한 예제 답변에서 방금 복사했습니다. 대부분의 답변은 6273에서 6848 사이의 점수를 갖습니다. 점수 계산에서 각각 같은 실수를하는 여러 답변이 있었으며 점수의 절반을 계산해야했습니다. (세 개의 충돌 단어가없는 경우 정확한 점수의 정확히 절반입니다.)
kasperd

1
예, 같은 실수를 저질렀습니다. 나중에 답변을 수정하겠습니다. 버스를 타야 해
Paul Chernoch

점수를 수정했습니다.
Paul Chernoch

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.