Update: Wie Jon darauf hingewiesen hat, behandelt der erste Ansatz Strings mit Wiederholung nicht sehr gut. Probleme treten auf, wenn doppelte Buchstabenpaare auftreten und das resultierende XOR 0 ist. Hier ist eine Modifikation, von der ich glaube, dass sie den ursprünglichen Algorithmus korrigiert. Es verwendet Euclid-Fermat sequences, paarweise Koprime-Ganzzahlen für jedes weitere Auftreten eines Zeichens in der Zeichenfolge zu generieren. Das Ergebnis ist, dass das XOR für doppelte Paare nicht Null ist.
Ich habe auch den Algorithmus etwas aufgeräumt. Beachten Sie, dass das Array, das die EF-Sequenzen enthält, nur Zeichen im Bereich 0x00 bis 0xFF unterstützt. Dies war nur eine billige Möglichkeit, den Algorithmus zu demonstrieren. Außerdem hat der Algorithmus noch die Laufzeit O (n), wobei n die Länge der Zeichenfolge ist.
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"));
}
Der Ausgang ist jetzt:
7F7D7F7F
7F7D7F7F
7F7D7F7F
7F417F4F
C796C7F0
E090E0F0
A909BB71
A959BB71
Erste Version (das ist nicht vollständig): Verwendung XOR, die (egal Reihenfolge) und ein anderer kleiner Trick kommutativ mit Koprime kombinieren geordnete Hashes von Buchstabenpaaren in der Zeichenfolge.Hier ist ein Beispiel 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()));
}
Die Ausgabe lautet:
4587590
4587590
4587590
7077996
Eek! Viel schwieriger, als ich dachte ... –
@Phil H: Haben Sie die aktualisierte Version meines Algorithmus unten betrachtet? Ich glaube, es ist einigermaßen vollständig, hat O (n) Laufzeit und kann leicht zu Arrays aus beliebigen hashbaren Elementen verallgemeinert werden. –