Какая хорошая хэш-функция для структуры с 3 беззнаковыми символами и целым числом для unordered_map?

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

В качестве побочного вопроса. Когда люди сравнивают неупорядоченную и упорядоченную карту, они никогда не говорят о хеш-функции, как это может быть? Не может ли плохая хэш-функция сделать неупорядоченную карту медленнее карты? (исключительно из-за хеш-функции)

struct exemple{

  unsigned char a,b,c;
  unsigned int n;

  bool operator == ( const exemple & other) const {..}
};

namespace std {
template <>
struct hash<exemple> : public std::unary_function<const exemple &, std::size_t>
{
    inline std::size_t operator()(const exemple & exemple_p ) const
    {
        return 0;// what do I do
    }
};

}

-edit- a,b,c может иметь только значения 'a', 'b', 'c' или 'd', а n варьируется от ~ 3 до 60.


person Icebone1000    schedule 15.11.2012    source источник
comment
Вы должны сами написать хэш-функцию?   -  person evanmcdonnal    schedule 15.11.2012
comment
Это два разных вопроса. Пожалуйста, опубликуйте один из них за один раз.   -  person Fred Foo    schedule 15.11.2012
comment
@evanmcdonnal, что ты имеешь в виду? неупорядоченная карта не компилируется, если я ее не предоставлю.   -  person Icebone1000    schedule 15.11.2012
comment
Да, но вы можете использовать логику уже существующей библиотеки хеширования. В качестве примера, скажем, у меня есть функция хеширования строк hash(string). Я мог бы создать функцию, которая преобразует int в строку, затем объединяет ее с тремя chars, а затем завершает функцию return hash(StringIjustMade);, что отличается от фактического написания логики хэширования низкого уровня самостоятельно. .   -  person evanmcdonnal    schedule 15.11.2012
comment
@evanmcdonnal не могли бы вы опубликовать ответ, показывающий это? Я никогда не использовал хеш-функцию напрямую   -  person Icebone1000    schedule 15.11.2012
comment
Сначала вы бросаете unary_function. Этот материал официально бесполезен с тех пор, как навсегда.   -  person pmr    schedule 15.11.2012
comment
@Icebone1000 Icebone1000 Я не собираюсь, потому что уже опубликовано два хороших решения.   -  person evanmcdonnal    schedule 15.11.2012


Ответы (4)


То, что вы делаете в своей хеш-функции, зависит от полученных вами значений, а не от их типов. Если бы все четыре члена данных содержали каждое значение, равномерно распределенное, я бы объединил два символа в unsigned long и вернул бы результат xoring двух значений:

typedef unsigned long ulong;
return n ^ (ulong(a << 16) | ulong(b << 8) | ulong(c));

Это, безусловно, хеш-функция. Другой вопрос, работает ли он хорошо. Вы также можете объединить результат с std::hash<unsigned long>.

person Dietmar Kühl    schedule 15.11.2012
comment
по поводу объединения хэшей: почему С++ 11 пропустил hash_combine? Это хорошая особенность boost::hash. - person pmr; 15.11.2012
comment
о, я понимаю ... я должен упомянуть, что мои беззнаковые символы могут принимать только значения a, b, c или d ... и n варьируется от ~ 3 до 60 - person Icebone1000; 15.11.2012
comment
Вы хотите сказать, что у вас есть 4‹sup›3‹/sup›*60 ~= 2^‹sup›12‹/sup› == 4096 значений? В этом случае не утруждайте себя использованием хеш-карты, а используйте массив... - person Dietmar Kühl; 15.11.2012
comment
@pmr: единственный след hash_combine, который я могу найти, находится в n3333. Другими словами: это не вы предлагали! Как и никто другой. - person Dietmar Kühl; 15.11.2012
comment
std::hash‹unsigned long› выдает ошибку преобразования, ошибка C2440: '‹function-style-cast›': невозможно преобразовать из 'unsigned long' в 'std::hash‹_Kty›' - person Icebone1000; 15.11.2012
comment
Я не знаю, что вы пытаетесь сделать, но это должно сработать: std::hash<unsigned long> hasher; unsigned long hash = hasher(value);. Судя по всему, вы пытаетесь передать unsigned long в качестве аргумента конструктора в std::hash<unsigned long>, то есть std::hash<unsigned long>(value). Это, конечно, не работает. - person Dietmar Kühl; 15.11.2012
comment
да, именно этим я и занимался - person Icebone1000; 15.11.2012

Вот базовая хэш-функция:

unsigned long long h = (n << 24) | (a << 16) | (b << 8) | c;
return std::hash(h);

То есть просто упакуйте участников в unsigned long long, а затем разгрузите работу в std::hash. В общем случае, когда int имеет ширину 32 бита, а long long - 64 бита, и если ваши символы не являются отрицательными, для хэша используется вся информация в ваших объектах.

person Fred Foo    schedule 15.11.2012

Считайте, что ваш struct в целом представляет собой строку байтов (7, если быть точным). Вы можете использовать любую общепринятую строковую хеш-функцию для этих 7 байтов. Вот общая хеш-функция битовой строки FNV (Fowler/Noll/Vo), примененная к вашему примеру (в данном классе хэш-функторов):

inline std::size_t operator()(const exemple& obj ) const
{
  const unsigned char* p = reinterpret_cast<const unsigned char*>( &obj );
  std::size_t h = 2166136261;

  for (unsigned int i = 0; i < sizeof(obj); ++i)
    h = (h * 16777619) ^ p[i];

  return h;
}

Обратите внимание, как я преобразовал ссылку на структуру exemple (obj) в указатель на const unsigned char, чтобы иметь доступ к байтам структуры один за другим, и я рассматриваю ее как непрозрачный двоичный объект. Обратите внимание, что sizeof(obj) на самом деле может быть 8, а не 7, в зависимости от заполнения компилятора (что означало бы, что где-то в структуре есть байт заполнения мусора, вероятно, между c и n. Если вы хотите, вы можете переписать хеш-функцию, чтобы перебирать a, b и c, а затем байты n по порядку (или в любом порядке), что устранит влияние любых байтов заполнения (которые могут существовать или не существовать) на хэш вашего struct.

Да, плохая хэш-функция может сделать unordered_map медленнее, чем ordered_map. Это не всегда обсуждается, потому что предполагается, что обобщенные быстрые алгоритмы, такие как приведенный выше хэш FNV, используются теми, кто использует unordered_map, и в этих случаях обычно unordered_map быстрее, чем ordered_map за счет возможности перебирать элементы контейнера по порядку. Однако да, вы должны использовать хорошую хеш-функцию для своих данных, и обычно достаточно использовать один из этих хорошо известных хэшей. В конечном счете, однако, у каждой хэш-функции есть свои недостатки, зависящие от распределения входных данных (здесь — содержимого структуры exemple).

Хорошее обсуждение обобщенного хеширования и примеры хэш-функций можно найти на странице Eternally Confuzzled, включая Хэш FNV в стиле C похож на тот, который я вам дал.

person Matthew Hall    schedule 15.11.2012

Для этой цели предназначен boost::hash_combine:

std::size_t hash = 0;
for (const auto& value : {a, b, c}) {
    boost::hash_combine(hash, value);
}
boost::hash_combine(hash, n);
return hash;
person Luke    schedule 11.04.2018