Временная сложность создания хэш-значения строки в хэш-таблице

Обычно говорится, что вставка и поиск строки в хэш-таблице - это O (1). Но как сделан хэш-ключ строки? Почему это не O (L), длина строки? Мне ясно, почему для целых чисел это O (1), но не для строк.

Обратите внимание, что я понимаю, почему вообще вставка в хэш-таблицу - это O (1), но я смущен, прежде чем вставлять хэш в таблицу, делая фазу хэш-значения.

И существует ли какая-либо разница между тем, как хэш-ключи для строк создаются между hashTable в java и unordered_map в С++?

Ответ 1

Вставка и т.д. в хэш-таблице - это O (1) в том смысле, что она является постоянной в количестве элементов в таблице.

"O (1)" в этом контексте не претендует на то, как быстро вы можете вычислить свои хэши. Если усилия для этого будут расти каким-то образом, так оно и есть. Тем не менее, я считаю маловероятным, что сложность хеш-функции приличного (т.е. "Пригодного для этого приложения" ) будет когда-либо хуже линейного по размеру (т.е. Длины в нашем примере строки) хэшируемого объекта.

Ответ 2

Обычно говорится, что вставка и поиск строки в хэш-таблице - это O (1). Но как сделан хэш-ключ строки? Почему это не O (L), длина строки? Мне ясно, почему для целых чисел это O (1), но не для строк.

Общее предложение O (1) означает, что время не увеличивается с количеством элементов в контейнере. Как вы говорите, время генерации хэш-значения из строки может быть не самой O (1) в длине строки, хотя для некоторых реализаций это: например, Microsoft С++ std::hash<std::string> имеет:

            size_t _Val = 2166136261U;
            size_t _First = 0;
            size_t _Last = _Keyval.size();
            size_t _Stride = 1 + _Last / 10;

            if (_Stride < _Last)
                    _Last -= _Stride;
            for(; _First < _Last; _First += _Stride)
                    _Val = 16777619U * _Val ^ (size_t)_Keyval[_First];
            return (_Val);

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

Стандартная библиотека GCC С++ использует другой подход: по крайней мере, в v4.7.2, он вызывает через класс поддержки _Hash_impl функцию static _Hash_bytes, который делает хеш мурмура, включающий каждый байт. GCC hash<std::string>, следовательно, O (N) в длине строки.

  • Более высокая приоритизация GCC минимизации столкновений также очевидна при использовании простых чисел ведер для std::unordered_set и std::unordered_map, реализация MS которых не выполняется - по крайней мере, до VS2013/VC12; общий подход MS будет более легким/быстрым для ключей, которые не подвержены столкновениям, но ухудшаются раньше и более резко в противном случае.

И существует ли какая-либо разница между тем, как хэш-ключи для строк создаются между hashTable в java и unordered_map в С++?

Как строки хэширования не заданы стандартом С++ - он оставлен для отдельных реализаций компилятора. Следовательно, разные компромиссы поражают разные компиляторы - даже разные версии одного и того же компилятора.

Документация David Pérez Cabrera ответит на ссылки на объяснение функции hashCode в Java:

Возвращает хэш-код для этой строки. Хэш-код для объекта String вычисляется как

 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

с использованием int арифметики, где s[i] является символом i th строки, n является длиной строки, а ^ указывает на возведение в степень. (Хэш-значение пустой строки равно нулю.)

Очевидно, что O (N) в длине строки.

Ответ 3

В соответствии с реализацией Java, Hashtable использует метод hashCode ключа (String или Integer). Hashtable String.hashCode Integer.hashCode

И С++ используют std::hash<std::string> или std::hash<int> в соответствии с http://en.cppreference.com/w/cpp/utility/hash, и реализация была в функциональном файле (/path/to/С++.../include/С++/4.8/functional)