new KeyValuePair <UInt32, UInt32> (i, j) .GetHashCode (); Hohe Rate von Duplikaten

Auf der Suche nach einem schnellen zusammengesetzten Schlüssel für Dictionary bin ich auf eine Anomalie gestoßen, die ich weder verstehen noch rechtfertigen kann.

In begrenzten Tests

Dictionary<KeyValuePair<UInt32, UInt32>, string>

ist deutlich langsamer (200: 1) als

Dictionary<KeyValuePair<UInt16, UInt16>, string>

Test auf zwei Schleifen von 0 bis 1000 Bestücken und dann ContainsKey

         Poplulate     ContainsKey  
UInt32    92085         86578  
UInt16     2201           431

Das Problem ist, dass

new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();

ergibt VIELE Duplikate.
In der Schleife von i und j 1024 werden nur 1024 eindeutige Hash-Werte erstellt.

Basierend auf dem Lawinenkommentar von CasperOne haben wir es mit i * 31 und j * 97 (zwei Primzahlen) versucht, und das ergab 105280 Unikate auf 1024X1024. Immer noch viele Duplikate. CasperOne Ich weiß, das ist nicht gleich Zufall. Es ist jedoch nicht meine Aufgabe, die Eingabe zufällig zu ordnen. GetHashCode () soll die Ausgabe randomisieren.

Warum die hohe Anzahl von Duplikaten?

Gleiche Schleife an

new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();

ergibt 1024 x 1024 eindeutige Hash-Codes (perfekt).

Int32 hat das gleiche Problem.

Diese doppelten Hashwerte töten

Dictionary<KeyValuePair<UInt32, UInt32>, string> 

Tuple generiert auch viele Duplikate, die bei Int32 im Vergleich zu Int16 nicht beeinträchtigt werden.

Die Zeit zum Generieren des unformatierten KVP und des unformatierten KPV.GetHashCode ist ähnlich.

Gleiche Anomalie mit HashSet.

Dictionary<KeyValuePair<UInt32, UInt32>, string> dKVPu32 = new Dictionary<KeyValuePair<UInt32, UInt32>, string>();
Dictionary<KeyValuePair<UInt16, UInt16>, string> dKVPu16 = new Dictionary<KeyValuePair<UInt16, UInt16>, string>();
KeyValuePair<UInt32, UInt32> kvpUint32;
KeyValuePair<UInt16, UInt16> kvpUint16;
int range = 1000;
Int32 hashCode;
HashSet<Int32> kvpUint32Hash = new HashSet<Int32>();
HashSet<Int32> kvpUint16Hash = new HashSet<Int32>();

Stopwatch sw = new Stopwatch();
sw.Start();
for (UInt32 i = 0; i < range; i++)
{
    for (UInt32 j = 0; j < range; j++)
    {
        kvpUint32 = new KeyValuePair<UInt32, UInt32>(i, j);
    }
}
Console.WriteLine("UInt32  raw " + sw.ElapsedMilliseconds.ToString());
//  7
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
    for (UInt16 j = 0; j < range; j++)
    {
        kvpUint16 = new KeyValuePair<UInt16, UInt16>(i, j);
    }
}
Console.WriteLine("UInt16  raw " + sw.ElapsedMilliseconds.ToString());
//  6
sw.Restart();
for (UInt32 i = 0; i < range; i++)
{
    for (UInt32 j = 0; j < range; j++)
    {
        hashCode = new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
        kvpUint32Hash.Add(hashCode);
    }
}
Console.WriteLine("UInt32  GetHashCode " + sw.ElapsedMilliseconds.ToString() + "  unique count " + kvpUint32Hash.Count.ToString());
//  285   1024
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
    for (UInt16 j = 0; j < range; j++)
    {
        hashCode = new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
        kvpUint16Hash.Add(hashCode);
    }
}
Console.WriteLine("UInt16  GetHashCode " + sw.ElapsedMilliseconds.ToString() + "  unique count " + kvpUint16Hash.Count.ToString());
//  398 1000000
sw.Restart();
Console.ReadLine();
for (UInt32 i = 0; i < range; i++)
{
    for (UInt32 j = 0; j < range; j++)
    {
        dKVPu32.Add(new KeyValuePair<UInt32, UInt32>(i, j), String.Format("{0} {1}", i.ToString(), j.ToString()));
    }
}
Console.WriteLine("hsKVPu32 pop " + sw.ElapsedMilliseconds.ToString());
//  92085
sw.Restart();
for (UInt32 i = 0; i < range; i++)
{
    for (UInt32 j = 0; j < range; j++)
    {
        if (!dKVPu32.ContainsKey(new KeyValuePair<UInt32, UInt32>(i, j))) Debug.WriteLine("Opps"); ;
    }
}
Console.WriteLine("hsKVPu32 find " + sw.ElapsedMilliseconds.ToString());
//  86578
dKVPu32.Clear();
dKVPu32 = null;
GC.Collect();
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
    for (UInt16 j = 0; j < range; j++)
    {
        dKVPu16.Add(new KeyValuePair<UInt16, UInt16>(i, j), String.Format("{0} {1}", i.ToString(), j.ToString()));
    }
}
Console.WriteLine("hsKVPu16 pop " + sw.ElapsedMilliseconds.ToString());
//   2201
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
    for (UInt16 j = 0; j < range; j++)
    {
        if (!dKVPu16.ContainsKey(new KeyValuePair<UInt16, UInt16>(i, j))) Debug.WriteLine("Opps"); ;
    }
}
sw.Stop();
Console.WriteLine("hsKVPu16 find " + sw.ElapsedMilliseconds.ToString());
//  431

P.S. Am schnellsten packt man .E.G. ((UInt32) int1 << 16) | int2;

Der Hash der ersten UInt32-Spalte entspricht dem Hash des KVP der nächsten beiden.

2281371105 8 992
2281371104 8 993
2281371107 8 994

2281371145 0 0
2281371147 0 2
2281371149 0 4
2281371151 0 6
2281371137 0 8

2281371144 0 1
2281371146 0 3
2281371148 0 5
2281371150 0 7
2281371136 0 9

2281371144 1 0
2281371145 1 1
2281371146 1 2
2281371147 1 3
2281371148 1 4
2281371149 1 5
2281371150 1 6
2281371151 1 7
2281371136 1 8
2281371137 1 9

2281371147 2 0
2281371146 2 1
2281371144 2 3
2281371151 2 4
2281371150 2 5
2281371149 2 6
2281371148 2 7
2281371139 2 8

Das einzige Muster, das ich gefunden habe, ist, dass entweder die Summe oder die Differenz oder der KVP übereinstimmt.
Konnte aber kein Muster finden, wann man summiert und wann man subtrahiert.
Es ist ein schlechter Hasch, also ist es von geringem Wert zu wissen, was es ist.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage