Actualización: Como señaló Jon, el primer enfoque no maneja las cadenas con la repetición muy bien. Los problemas surgen cuando se encuentran pares duplicados de letras y el XOR resultante es 0. Aquí hay una modificación que creo que corrige el algoritmo original. Utiliza Euclid-Fermat sequences para generar pares enteros coprime para cada ocurrencia adicional de un carácter en la cadena. El resultado es que el XOR para los pares duplicados no es cero.
También he limpiado el algoritmo ligeramente. Tenga en cuenta que la matriz que contiene las secuencias EF solo admite caracteres en el rango de 0x00 a 0xFF. Esta era solo una forma barata de demostrar el algoritmo. Además, el algoritmo todavía tiene el tiempo de ejecución O (n) donde n es la longitud de la cadena.
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"));
}
La salida es ahora:
7F7D7F7F
7F7D7F7F
7F7D7F7F
7F417F4F
C796C7F0
E090E0F0
A909BB71
A959BB71
primera versión (que no está completo): Uso XOR que es conmutativa (el orden no importa) y otro pequeño truco involucrando coprimes para combinar hashes ordenados de pares de letras en la cadena.Este es un ejemplo en 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()));
}
La salida es:
4587590
4587590
4587590
7077996
Eek! Mucho más complicado de lo que pensaba ... –
@Phil H: ¿Has considerado la versión actualizada de mi algoritmo a continuación? Creo que es razonablemente completo, tiene tiempo de ejecución O (n) y se puede generalizar fácilmente a las matrices de cualquier elemento hashable. –