Почему диктон python реализован в виде хеш-таблицы, тогда как std :: map основан на дереве? - PullRequest
14 голосов
/ 25 ноября 2011

Почему один язык использует дерево, а другой использует хеш-таблицу для, казалось бы, похожей структуры данных?

Карта c ++ против dict python

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

Дерево гарантированно будет иметь O (log n).
Принимая во внимание, что хеш-таблица не гарантирует, если входные данные ранее не известны из-за возможных коллизий.
Я склонен думать, что производительность хеш-таблицы станет близкойк O (n), поскольку размер проблемы становится больше.
Потому что я не слышал о хеш-функции, которая динамически корректирует размер таблицы по мере увеличения размера проблемы.

Следовательно, хеш-таблица полезна только для определенного диапазона размера проблемы, и поэтому большинство БД использует дерево, а не хеш-таблицу.

Ответы [ 4 ]

20 голосов
/ 25 ноября 2011

Новый стандарт C ++ имеет тип std::unordered_map , который является хеш-таблицей . IIRC они хотели, чтобы он также вошел в предыдущий стандарт, но во время обсуждений не было достаточно времени, поэтому он был опущен. Однако большинство популярных компиляторов предоставляли его тем или иным способом годами.

Другими словами, не беспокойтесь об этом слишком сильно. Используйте правильную структуру данных для поставленной задачи.


Что касается вашего понимания хеш-таблиц, оно неточно:

Я не слышал о хеш-функции, которая динамически корректирует свою таблицу размер по мере роста проблемы

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

13 голосов
/ 25 ноября 2011

Ваше понимание хеш-таблиц (и кто их использует) неверно.

Проблема в том, что хеш-таблица является довольно расплывчатым термином.Под капотом есть много реализаций ... но сначала давайте поговорим об использовании BST (деревьев двоичного поиска).


Почему C ++ использует дерево двоичного поиска?

C ++ разработан комитетом, существует много возможных реализаций хеш-таблиц, приводящих к очень различным характеристикам, в то время как наиболее популярные реализации BST (Red-Black Tree и AVL Tree) имеют почти идентичные характеристики.Поэтому они не отклонили хеш-таблицы напрямую, они просто не могли определиться с характеристиками, которые нужно выбрать, и деталями, которые будут представлены пользователю.

См. Комментарий Джеймса Канзе, предложение поступило слишком поздно, и Джеймсзадает интересный вопрос о том, почему Степанов не предложил его первым.Я все еще подозреваю, что виноват ряд вариантов.

Почему базы данных используют деревья поиска?

Прежде всего, давайте остановимся на программном обеспечении базы данных.Я выберу Oracle, потому что он широко документирован и типичен для баз данных SQL.Oracle предлагает два типа индексов: растровые и поисковые деревья.

Примечание: они не используют деревья поиска BINARY, а вместо этого используют деревья B +, которые намного более удобны для ввода-вывода и кеша

Существует принципиальная разница между хэш-таблицей и деревом поиска: последняя сортируется.Многие операции с базами данных подразумевают сортировку:

  • получить n-й элемент
  • получить верхние n элементов
  • получить элементы в [a, b]

Во всех этих случаях хэш-таблица бесполезна.

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

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


Теперь, что касается производительности.

Действительно, производительность деревьев поиска, как правило, хорошо известна и понятна. O (журнал N) хорош и опрятен.

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

Простой пример структуры, которую может использовать хеш-таблица:

  • Открытая адресация: хеш-таблица представляет собой массив элементов, хеш-код указывает слот массива, в который следует поместить элемент, если слот заполнен, существует стратегия для определения другого слота.Та же стратегия используется для поиска.
  • Buckets: хеш-таблица представляет собой массив указателей на сегменты, хеш-код указывает слот сегмента, в который помещаются элементы.Предполагается, что сегмент может расти бесконечно.

Эти две стратегии имеют чрезвычайно разные характеристики, и последние характеристики также зависят от реализаций сегментов (простая реализация заключается в использовании простого связанного списка).

Но даже если вы выберете реализацию, ее производительность зависит от распределения хеш-функций, которое зависит от самой последовательности ввода!


Мой личный совет?Чтобы выбрать между unordered_map и map в C ++, я просто спрашиваю себя, нужны ли мне отсортированные элементы или нет.Если мне нужно, чтобы они были отсортированы, я использую map, в противном случае я использую unordered_map.В большинстве случаев производительность так же хороша, так что это просто семантика .

5 голосов
/ 25 ноября 2011

Это более или менее произвольный выбор языковых дизайнеров.В случае C ++ я подозреваю (но не знаю наверняка), что мотивацией было желание определить строгие верхние пределы сложности: разработка хорошей хеш-функции не тривиальна, а хеш-таблица с плохой хеш-функциейвыполняет очень плохо.Другой проблемой, которая могла бы быть рассмотрена, является тот факт, что существует установленный оператор для упорядочения (<);нет ничего похожего на хеширование.

В случае с Python (и многими другими языками) в большинстве случаев ключи будут встроенными, как str (std::string былонедоступно при определении STL), поэтому вы можете обеспечить адекватную хеш-функцию.И когда все является объектом и наследуется от общего базового класса, вы можете легко определить стандартный интерфейс для hash, определив (виртуальную) функцию в универсальном базовом классе.

Наконец, C ++решение зависит от одной функции / оператора;хеш-таблица требует двух (хеш-функция и равенство), которые должны быть совместимы, что более подвержено ошибкам.Распространенной ошибкой в ​​Java является определение equals, но не определение hashCode;Я подозреваю, что есть классы Python, которые допускают ту же ошибку (определяя __cmp__ или __eq__, но не __hash__).Конечно, учитывая количество случаев, когда люди путают оператор < в C ++, я не уверен, что это также безопасно: -).

4 голосов
/ 25 ноября 2011

Python хеш-таблицы никогда не заполнены более чем на 2/3.Изменение размера по мере их роста (начиная с размера 8, затем в четыре раза по размеру до 50000, а затем удваивается).Это дает им амортизированную вставку, удаление и поиск O (1).Чрезмерные столкновения возможны, но редки.

...