new KeyValuePair <UInt32, UInt32> (i, j) .GetHashCode (); Wysoki współczynnik duplikatów

W poszukiwaniu szybkiego klucza złożonego dla Słownika natknąłem się na anomalię, której nie mogę zrozumieć ani usprawiedliwić.

W ograniczonych testach

Dictionary<KeyValuePair<UInt32, UInt32>, string>

jest znacznie wolniejszy (200: 1) niż

Dictionary<KeyValuePair<UInt16, UInt16>, string>

Przetestuj na dwóch pętlach od 0 do 1000 Wypełnij, a następnie ContainsKey

         Poplulate     ContainsKey  
UInt32    92085         86578  
UInt16     2201           431

Problemem jest

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

daje WIELE duplikatów.
W pętli i i j 1024 tworzonych jest tylko 1024 unikalnych wartości skrótu.

Na podstawie komentarza lawinowego CasperOne próbowano i * 31 i j * 97 (dwie liczby pierwsze), co spowodowało, że na 1024X1024 było 105280 unikalnych. Nadal wiele duplikatów. CasperOne Wiem, że to nie to samo, co losowe. Ale nie jest moim zadaniem losowanie danych wejściowych. GetHashCode () ma losować dane wyjściowe.

Dlaczego duża liczba duplikatów?

Ta sama pętla

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

daje 1024 X 1024 unikalnych kodów skrótu (idealne).

Int32 ma ten sam problem.

Te zduplikowane wartości skrótu zabijają

Dictionary<KeyValuePair<UInt32, UInt32>, string> 

Tuple generuje również wiele duplikatów, które nie ulegają degradacji w Int32 w porównaniu z Int16.

Czas generowania surowego KVP i surowego KPV.GetHashCode jest podobny.

Ta sama anomalia z 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. Najszybszy jest spakowanie .E.G. ((UInt32) int1 << 16) | int2;

Wartość mieszania pierwszej kolumny UInt32 równa się hashowi KVP z następnych dwóch.

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

Jedyny wzór, jaki znalazłem, to to, że albo suma, albo różnica, albo KVP pasują.
Ale nie mógł znaleźć wzoru, kiedy sumować i kiedy odejmować.
Jest to zły hash, więc wiedza o tym, co to jest, ma małą wartość.

questionAnswers(4)

yourAnswerToTheQuestion