Что является подходящим алгоритмом GetHashCode() для двумерной точечной структуры (избегая столкновений)

Рассмотрим следующий код:

struct Vec2 : IEquatable<Vec2>
{
    double X,Y;

    public bool Equals(Vec2 other)
    {
        return X.Equals(other.X) && Y.Equals(other.Y);
    }

    public override bool Equals(object obj)
    {
        if (obj is Vec2)
        {
            return Equals((Vec2)obj);
        }
        return false;
    }

    // this will return the same value when X, Y are swapped
    public override int GetHashCode()
    {
        return X.GetHashCode() ^ Y.GetHashCode();
    }

}

Помимо беседы о сравнении удвоений для равенства (это всего лишь демонстрационный код), я обеспокоен тем, что есть хеш-столкновение, когда значения X, Y меняются местами. Например:

Vec2 A = new Vec2() { X=1, Y=5 };
Vec2 B = new Vec2() { X=5, Y=1 };

bool test1 = A.Equals(B);  // returns false;
bool test2 = A.GetHashCode() == B.GetHashCode() // returns true !!!!!

который должен разрушить хаос в коллекции словарей. Поэтому вопрос заключается в том, как свойство формирует функцию GetHashCode() для 2,3 или даже 4 значений с плавающей запятой, так что результаты не симметричны, а хеши не сталкиваются.

Изменить 1:

Point реализует неподходящее решение x ^ y и PointF wraps ValueType.GetHashCode().

Rectangle имеет очень своеобразное выражение (((X ^ ((Y << 13) | (Y >> 19))) ^ ((Width << 26) | (Width >> 6))) ^ ((Height << 7) | (Height >> 25))) для хеш-кода, который, как представляется, выполняет, как ожидалось.

Изменить 2:

"System.Double" имеет приятную реализацию, так как не учитывает каждый бит одинаково важный

public override unsafe int GetHashCode() //from System.Double
{
    double num = this;
    if (num == 0.0)
    {
        return 0;
    }
    long num2 = *((long*) &num);
    return (((int) num2) ^ ((int) (num2 >> 32)));
}

Ответ 1

Джон skeet получил это:

Каков наилучший алгоритм для переопределенного System.Object.GetHashCode?

   public override int GetHashCode()
   {
       unchecked // Overflow is fine, just wrap
       {
           int hash = 17;
           // Suitable nullity checks etc, of course :)
           hash = hash * 23 + X.GetHashCode();
           hash = hash * 23 + Y.GetHashCode();
           return hash;
       }
   }

Кроме того, измените реализацию Equals(object) на:

return Equals(obj as FVector2);

Обратите внимание, однако, что это может воспринимать производный тип равным. Если вы этого не хотите, вам придется сравнивать тип времени выполнения other.GetType() с typeof(FVector2) (и не забывать проверки недействительности) Спасибо за указание на структуру, LukH

У Resharper есть хорошая генерация кода для равенства и хеш-кода, поэтому, если у вас есть resharper, вы можете позволить этому сделать свою вещь

Ответ 2

Столкновение хэшей не приводит к хаосу в коллекции словарей. Они уменьшат эффективность, если вам не повезет, чтобы получить их, но словари должны справляться с ними.

Столкновения должны быть редки, если это вообще возможно, но они не означают, что реализация неверна. XORs часто плохо по причинам, которые вы дали (высокие столкновения) - ohadsc опубликовал образец, который я дал ранее для альтернативы, и это должно быть хорошо.

Обратите внимание, что реализовать Vec2 невозможно без столкновений - возможны только 2 32 возможные значения возврата из GetHashCode, но есть более вероятные значения X и Y, даже после удаления NaN и бесконечных значений...

Эрик Липперт имеет последнее сообщение в блоге в GetHashCode, которое может показаться вам полезным.

Ответ 3

Каковы разумные оценки для координат?

Если это не могут быть все возможные целые значения, вы можете просто:

const SOME_LARGE_NUMBER = 100000; return SOME_LARGE_NUMBER * x + y;

Ответ 4

Если размер вашего хеш-кода меньше размера вашей структуры, тогда столкновения неизбежны в любом случае.

Ответ 5

Подход хэш-кодов работает для межсетевых координат, но не рекомендуется для значений с плавающей запятой. С помощью координат с плавающей запятой можно создать набор точек/пул, используя отсортированную структуру последовательности.

Сортированная последовательность - это сбалансированное двоичное дерево с листовой версией.

Здесь ключи будут координатами точки.