А в чем профит хешевых контейнеров?

apl13

1. Для поиска ключа нужно посчитать его хеш.
2. Повседневная мудрость подсказывает, что стоимость подсчета хеша ключа длины n по крайней мере O(n)*.
3. Для достаточно большого контейнера длина ключа будет в среднем не ниже логарифма размера контейнера
(собственно, это обстоятельство делает печальными и префиксные деревья, и прочие способы хитро и быстро хранить).
4. При этом в винтажном хеше с бакетами нас ждет еще вполне линейный поиск в ведре.
То есть получается, закрывая глаза на коэффициенты, асимптотика хешей не лучше, чем у балансирующихся деревьев.
* Вполне возможно, что в данной задаче хеши хорошо распределяются, даже если считать их по какой-нибудь маленькой проекции множества ключей; но эту же информацию можно использовать и для поиска по деревьям.
У меня перед глазами как живое стоит описание векторов в Скале, где не моргнув глазом пишут, что поскольку индекс — это инт, то хранить элементы в дереве по пять бит на уровень дает константный доступ. Ну в самом деле, число хопов ограничено сверху логарифмом по основанию 32, а для 32-битных чисел это, очевидно, ограничено константой.

kokoc88

Повседневная мудрость подсказывает, что стоимость подсчета хеша ключа длины n по крайней мере O(n)*.
А сравнение для дерева за O(1)?

apl13

То есть, для дерева — log²(N для хеша без коллизий — log(N так?

kokoc88

То есть, для дерева — log²(N для хеша без коллизий — log(N так?
Прости, не понял, что это за оценки. Можешь пояснить?

Garryss

Сильно зависит от специфики задачи. В среднем по больнице, если сравнивать pure hash table с pure rb-tree, то hash table выигривает.
Деревья (по крайней мере низко-n-арные) плохи тем, что по памяти нужно много прыгать. А память сейчас — это те же самые проблемы, что и HDD 20 лет назад, — сильно плохое latency, 200+ тактов на доступ.
Доступ в хэш-таблице — это O(1) amortized time. Ситуация, когда тебе по бакетам линейно нужно ходить, неестественна. В джаве вот стандартный HashMap увеличивает количество бакетов при заполнении >= 75%, в cpp >= 100%.

apl13

Прости, не понял, что это за оценки. Можешь пояснить?
Сферический поиск в большом контейнере, а ч0?
Хеш логарифмический, как я писал.
В дереве логарифмическое число хопов, и на каждый, как ты писал, логарифмическое сравнение.

Garryss

В добавок я не понял, как ты пытаешься сравнивать доступ в контейнер (O(log n) vs O(1 с подсчетом хэша (O(n. Ты ведь понимаешь, что 'n' в этих двух случаях — это разные 'n'?

apl13

А память сейчас — это те же самые проблемы, что и HDD 20 лет назад, — сильно плохое latency, 200+ тактов на доступ.
А в идеальной памяти у хеша есть преимущество перед деревом?

apl13

Ты ведь понимаешь, что 'n' в этих двух случаях — это разные 'n'?
Читай, как я это делаю:
стоимость подсчета хеша ключа длины n по крайней мере O(n)
n в этой фразе связанная переменная.
Размер контейнера в другом посте я назвал N.
Заглавная буква, потому что большой контейнер.
Строчная буква, потому что недлинный ключ.

apl13

Ситуация, когда тебе по бакетам линейно нужно ходить, неестественна. В джаве вот стандартный HashMap увеличивает количество бакетов при заполнении >= 75%, в cpp >= 100%.
75%, по-моему, уже не о том. Это не с бакетами хеш же.
В славные времена бакетов, вроде, считалось 8–10 средняя заполненность корзины порог для рехешинга.

Garryss

Это доступ в которую бесплатен?
Тогда уже нужно сравнивать время подсчета хэша (один раз на доступ) с временем работы компаратора (коих log n на доступ).
BTW, для объектов, где время подсчета хэша существенно, обычно хэш делают полем класса. Что избавляет каждый раз его считать при доступе в хэшированный контейнер.

kokoc88

Сферический поиск в большом контейнере, а ч0?
Ну если принять твои определения, то для 10^6 элементов ты получаешь 20 операций против 400. Вполне ощутимый профит.

Garryss

Да все тот же самый :)
C++
Java

kokoc88

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

apl13

Ну я и говорю. Тогда да.
Плюс тут предлагает прямо в объекте его хеш мемоизировать, тоже профит.

apl13

М-да.
В такой постановке hash map становится гирляндой пустых ведер. Omg.
Как это все звенит при малейшем порыве ветра.

apl13

Да, но от хотя бы одного сравнения это нас не спасает.
Ну то есть остается тот же log(N) для хеша, разве что без мемоизации это было 2log(N).

Garryss

Что такое "N" в следующей фразе?
Ну то есть остается тот же log(N) для хеша

Maurog

не лучше
чтобы сделать подобный вывод нужно очень сильно зажмуриться :grin:
если коротко, то профит хешированных контейнеров в хорошей (в частности, лучшей скорости работы по сравнению с балансированными деревьями) скорости при определенных обстоятельствах (то есть в конкретных ситуациях)
аналогичный плюс есть у деревьев :grin:
факторы, которые влияют на скорость:
1) сложность подсчета хеша
2) сложность сравнения элементов
3) кол-во колизий
чем ближе хешированный контейнер походит на разреженный массив, тем шустрее идет поиск (получаем почти произвольный доступ). дерево ну никак к этому не приблизится (оно всегда развесистое и надо log скачков\сравнений сделать для поиска глубоких элементов)
эти мысли в основном о поиске элементов. для операций модификации контейнера нужно отдельно взвешивать сложность рехешинга и балансировки. в целом, факторов так много, что асимптотически здесь сложно выдать какие-то оценки. проще рассмотреть лучшие\худшие варианты для обоих видов контейнера

apl13

если коротко, то профит хешированных контейнеров в хорошей (в частности, лучшей скорости работы по сравнению с балансированными деревьями) скорости при определенных обстоятельствах (то есть в конкретных ситуациях)
аналогичный плюс есть у деревьев :grin:
Короче, получился очередной тред ниачом.

Maurog

получился очередной тред ниачом.
так точно
пытаться сравнить теплое с мягким, да еще и с учетом возможной аллергии :grin:

apl13

Ну на самом деле польза треда очевидна. Я хотя бы узнал, что в своих прикидках забыл про стоимость сравнения.

vall

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

apl13

Но это повседневность, а не асимптотика.

vall

асимптотически всё повседневность — все умрут :smirk:

evgen5555

Для хранилищ в KVS больших объемов балансировка на каждой вставке (delete для простоты можно просто считать недоступной операцией) будет просто нагружать систему сверх положенного с минимальной пользой.

stm5872449

Для хранилищ в KVS больших объемов балансировка на каждой вставке (delete для простоты можно просто считать недоступной операцией) будет просто нагружать систему сверх положенного с минимальной пользой.
RB-дерево не балансируется на каждой вставке.

evgen5555

Ну афтар вроде не только rb имел в виду, а мб любые двоичные

zibrov

1. Верно.
2. Ну да, если у тебя строка длины n, то требуется порядка n операций чтобы найти её хэш.
3. Вот тут по-подробнее пожалуйста. Каким образом длина контейнера влияет на длину строки, которую ты там собираешься искать? Если у тебя строки в среднем длины n, а количество элементов в контейнере m, то поиск будет в среднем за O(n). Деревья дадут оценку O(n*logm т. е. всё ок, хэши круче, как и должно быть.
4. Вообще не расшифровал эту фразу. Пусть N - количество элементов в контейнере. В хэшах в худшем случае - поиск осуществляется за O(N). В среднем - за O(1). На практике, сильное замедление замедление хэшей - это скорее какой-то искусственный тест.
Во всяких rb-tree ассимптотика и в среднем и в худшем O(logN).
И ещё, где-то обсуждалось, что открытая адресация на int-ах круче бакетов по скорости, вот :) Но тут конечно всего лишь вопрос константы.

apl13

1. Верно.
2. Ну да, если у тебя строка длины n, то требуется порядка n операций чтобы найти её хэш.
3. Вот тут по-подробнее пожалуйста. Каким образом длина контейнера влияет на длину строки, которую ты там собираешься искать? Если у тебя строки в среднем длины n, а количество элементов в контейнере m, то поиск будет в среднем за O(n). Деревья дадут оценку O(n*logm т. е. всё ок, хэши круче, как и должно быть.
4. Вообще не расшифровал эту фразу. Пусть N - количество элементов в контейнере. В хэшах в худшем случае - поиск осуществляется за O(N). В среднем - за O(1). На практике, сильное замедление замедление хэшей - это скорее какой-то искусственный тест.
Во всяких rb-tree ассимптотика и в среднем и в худшем O(logN).
И ещё, где-то обсуждалось, что открытая адресация на int-ах круче бакетов по скорости, вот :) Но тут конечно всего лишь вопрос константы.
Короче, после второго тезиса сломался, да?

apl13

Каким образом длина контейнера влияет на длину строки, которую ты там собираешься искать?
Давай так.
Издалека.
У тебя есть 256 записей в таблице.
Сколько бит нужно как минимум для ключей, чтобы точно закодировать их все?

zibrov

Ок, если у тебя в таблице N записей, то офк нужно иметь logN информации. И всё-таки, при чём здесь длина строк?
А вот в первом сообщении ты под ключом подразумевал явно совершенно другое:
3. Для достаточно большого контейнера длина ключа будет в среднем не ниже логарифма размера контейнера

apl13

Ок, если у тебя в таблице N записей, то офк нужно иметь logN информации. И всё-таки, при чём здесь длина строк?
Да, извини, я сказал про биты, и ты совсем запутался, сразу представив деревья.
Давай сделаем проще.
Забудем про деревья и хеши.
Представь, что у тебя есть словарь совершенно нового, незнакомого тебе, чтобы не путаться, языка.
Бумажный словарь. Обычный бумажный словарь. Издание Мюллера.
Алфавит языка состоит из двадцати букв.
В словаре восемь тысяч слов.
Какова будет средняя длина слова?

Dasar

Какова будет средняя длина слова?
7 букв
Оставить комментарий
Имя или ник:
Комментарий: