我想将字符串哈希成整数以便将其放入数组中。然而,我对哈希函数并不太了解,因此我的当前方法只是将所有字符的ASCII码值相加,并将其除以数组大小取余。
是否有更简单、更快速、更好的方法?
我想将字符串哈希成整数以便将其放入数组中。然而,我对哈希函数并不太了解,因此我的当前方法只是将所有字符的ASCII码值相加,并将其除以数组大小取余。
是否有更简单、更快速、更好的方法?
FNV-1a哈希算法快速而易于实现。
IniFiles.pas
单元中TStringHash.HashOf
方法使用的版本。其中包括一个更快的汇编版本:function HashOf(P: PByteArray; Len: integer): cardinal;
// algorithm from IniFiles.TStringHash.HashOf
{$ifdef PUREPASCAL}
var I: Integer;
begin
Result := 0;
for I := 1 to Len do
Result := ((Result shl 2) or (Result shr (SizeOf(Result)*8-2))) xor P[I];
end;
{$else}
asm // faster asm version by Synopse
or edx,edx
jz @z
push ebx
mov ebx,edx // ebx = length(Key)
mov edx,eax // edx = Text
xor eax,eax // eax = Result
xor ecx,ecx // ecx = Result shl 2 = 0
@1: shr eax,$1e // eax = Result shr (SizeOf(Result) * 8 - 2))
or ecx,eax // ecx = ((Result shl 2) or (Result shr (SizeOf(Result)*8-2)))
movzx eax,byte ptr [edx] // eax = ord(Key[i])
inc edx
xor eax,ecx // eax = () xor ord(Key[i])
dec ebx
lea ecx,[eax*4] // ecx = Result shl 2
jnz @1
pop ebx
@z:
end;
{$endif}
来自《C程序设计语言》第三版的经典 Kernighan & Ritchie 哈希算法 - 不是最好的,但是代码简单高效。
function kr32(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal;
var i: integer;
begin
for i := 0 to len-1 do
crc := ord(buf[i])+crc*31;
result := crc;
end;
在zlib中实现的快速“Adler”循环冗余校验 - 优化的汇编版本在这里:
function Adler32Pas(Adler: cardinal; p: pointer; Count: Integer): cardinal;
var s1, s2: cardinal;
i, n: integer;
begin
s1 := LongRec(Adler).Lo;
s2 := LongRec(Adler).Hi;
while Count>0 do begin
if Count<5552 then
n := Count else
n := 5552;
for i := 1 to n do begin
inc(s1,pByte(p)^);
inc(cardinal(p));
inc(s2,s1);
end;
s1 := s1 mod 65521;
s2 := s2 mod 65521;
dec(Count,n);
end;
result := word(s1)+cardinal(word(s2)) shl 16;
end;
我的自己更快的变体 - 不可重入,但更快,因为它将通过DWORD进行读取 - 以及一个更快的汇编版本在这里:
function Hash32(Data: pointer; Len: integer): cardinal;
function SubHash(P: PCardinalArray; L: integer): cardinal;
{$ifdef HASINLINE}inline;{$endif}
var s1,s2: cardinal;
i: PtrInt;
const Mask: array[0..3] of cardinal = (0,$ff,$ffff,$ffffff);
begin
if P<>nil then begin
s1 := 0;
s2 := 0;
for i := 1 to L shr 4 do begin // 16 bytes (4 DWORD) by loop - aligned read
inc(s1,P^[0]);
inc(s2,s1);
inc(s1,P^[1]);
inc(s2,s1);
inc(s1,P^[2]);
inc(s2,s1);
inc(s1,P^[3]);
inc(s2,s1);
inc(PtrUInt(P),16);
end;
for i := 1 to (L shr 2)and 3 do begin // 4 bytes (DWORD) by loop
inc(s1,P^[0]);
inc(s2,s1);
inc(PtrUInt(P),4);
end;
inc(s1,P^[0] and Mask[L and 3]); // remaining 0..3 bytes
inc(s2,s1);
result := s1 xor (s2 shl 16);
end else
result := 0;
end;
begin // use a sub function for better code generation under Delphi
result := SubHash(Data,Len);
end;
经典的CRC32版本 - 您可以在此处找到一个非常 优化的汇编版本(使用8个表):
function UpdateCrc32(aCRC32: cardinal; inBuf: pointer; inLen: integer) : cardinal;
var i: integer;
begin
result := aCRC32;
// if we used a dynamic table, we assume we want shorter code size
for i := 1 to inLen do begin
result := crc32Tab[byte(result xor pByte(inBuf)^)] xor (result shr 8);
inc(cardinal(inBuf));
end;
end;
它非常快。
它的分布和雪崩特性对于非加密哈希来说非常好。
即使在最坏情况下,它的表现仍然相当不错。
function Murmur2(const S: AnsiString; const Seed: LongWord=$9747b28c): LongWord;
var
h: LongWord;
len: LongWord;
k: LongWord;
data: Integer;
const
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
m = $5bd1e995;
r = 24;
begin
len := Length(S);
//The default seed, $9747b28c, is from the original C library
// Initialize the hash to a 'random' value
h := seed xor len;
// Mix 4 bytes at a time into the hash
data := 1;
while(len >= 4) do
begin
k := PLongWord(@S[data])^;
k := k*m;
k := k xor (k shr r);
k := k* m;
h := h*m;
h := h xor k;
data := data+4;
len := len-4;
end;
{ Handle the last few bytes of the input array
S: ... $69 $18 $2f
}
Assert(len <= 3);
if len = 3 then
h := h xor (LongWord(s[data+2]) shl 16);
if len >= 2 then
h := h xor (LongWord(s[data+1]) shl 8);
if len >= 1 then
begin
h := h xor (LongWord(s[data]));
h := h * m;
end;
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h := h xor (h shr 13);
h := h * m;
h := h xor (h shr 15);
Result := h;
end;
通过所有原始C实现的自我测试。
Jenkins哈希函数可以帮助您入门。
我的当前方法只是将所有字符的ASCII码相加,然后取模数组大小。
这样做会丢失重要的信息,即字符在字符串中的位置。这是一个不好的想法,因为字符串“AB”和“BA”将具有相同的哈希值。
与简单的加法相比,保持它的原始性,可以使用表达式hash = hash*P1 + str[i]*P2 + P3;
,其中Pi是一些质数。如果我需要快速哈希函数,我就是这么做的。我经常使用7、5和3作为质数,但数字应该明显调整(以及hash
的初始值),以便哈希函数的结果可用于您的任务。
有关更多信息,请阅读对应的(而且相当信息丰富的)维基百科文章。
我尝试了许多快速哈希函数,最终选择了这个:
function StrHash(const st:string):cardinal;
var
i:integer;
begin
result:=0;
for i:=1 to length(st) do
result:=result*$20844 xor byte(st[i]);
end;