Aggiornamento: Come ha sottolineato Jon, il primo approccio non gestisce molto bene le stringhe con ripetizione. I problemi sorgono quando si incontrano coppie di lettere duplicate e lo XOR risultante è 0. Ecco una modifica che credo corregge l'algoritmo originale. Utilizza Euclid-Fermat sequences per generare interi coprimi paralleli per ogni occorrenza aggiuntiva di un carattere nella stringa. Il risultato è che lo XOR per coppie duplicate è diverso da zero.
Ho anche ripulito leggermente l'algoritmo. Si noti che l'array contenente le sequenze EF supporta solo caratteri nell'intervallo da 0x00 a 0xFF. Questo era solo un modo economico per dimostrare l'algoritmo. Inoltre, l'algoritmo ha ancora il tempo di esecuzione O (n) dove n è la lunghezza della stringa.
static int Hash(string s)
{
int H = 0;
if (s.Length > 0)
{
//any arbitrary coprime numbers
int a = s.Length, b = s.Length + 1;
//an array of Euclid-Fermat sequences to generate additional coprimes for each duplicate character occurrence
int[] c = new int[0xFF];
for (int i = 1; i < c.Length; i++)
{
c[i] = i + 1;
}
Func<char, int> NextCoprime = (x) => c[x] = (c[x] - x) * c[x] + x;
Func<char, char, int> NextPair = (x, y) => a * NextCoprime(x) * x.GetHashCode() + b * y.GetHashCode();
//for i=0 we need to wrap around to the last character
H = NextPair(s[s.Length - 1], s[0]);
//for i=1...n we use the previous character
for (int i = 1; i < s.Length; i++)
{
H ^= NextPair(s[i - 1], s[i]);
}
}
return H;
}
static void Main(string[] args)
{
Console.WriteLine("{0:X8}", Hash("abcdef"));
Console.WriteLine("{0:X8}", Hash("bcdefa"));
Console.WriteLine("{0:X8}", Hash("cdefab"));
Console.WriteLine("{0:X8}", Hash("cdfeab"));
Console.WriteLine("{0:X8}", Hash("a0a0"));
Console.WriteLine("{0:X8}", Hash("1010"));
Console.WriteLine("{0:X8}", Hash("0abc0def0ghi"));
Console.WriteLine("{0:X8}", Hash("0def0abc0ghi"));
}
L'uscita è ora:
7F7D7F7F
7F7D7F7F
7F7D7F7F
7F417F4F
C796C7F0
E090E0F0
A909BB71
A959BB71
Prima versione (che non è completa): Usare XOR che è commutativa (ordine non importa) e un altro piccolo trucco coinvolgere coprimi per combinare gli hash ordinati di coppie di lettere nella stringa.Ecco un esempio in C#:
static int Hash(char[] s)
{
//any arbitrary coprime numbers
const int a = 7, b = 13;
int H = 0;
if (s.Length > 0)
{
//for i=0 we need to wrap around to the last character
H ^= (a * s[s.Length - 1].GetHashCode()) + (b * s[0].GetHashCode());
//for i=1...n we use the previous character
for (int i = 1; i < s.Length; i++)
{
H ^= (a * s[i - 1].GetHashCode()) + (b * s[i].GetHashCode());
}
}
return H;
}
static void Main(string[] args)
{
Console.WriteLine(Hash("abcdef".ToCharArray()));
Console.WriteLine(Hash("bcdefa".ToCharArray()));
Console.WriteLine(Hash("cdefab".ToCharArray()));
Console.WriteLine(Hash("cdfeab".ToCharArray()));
}
L'output è:
4587590
4587590
4587590
7077996
fonte
2010-04-06 13:44:23
Eek! Molto più complicato di quanto pensassi ... –
@Phil H: hai considerato la versione aggiornata del mio algoritmo di seguito? Credo che sia ragionevolmente completo, ha O (n) runtime e può essere facilmente generalizzato agli array di qualsiasi elemento lavabile. –