Необходимо разработать ассоциативный контейнер hash_map<Key, Data, HashFcn, Equal, Alloc> который будет ассоциировать
объект типа Key с объектом типа Data. Интерфейс этого контейнера должен быть аналогичным к STL map,
только оператор < должен быть заменен оператором ==
Здравствуйте, eBit, Вы писали:
B>Необходимо разработать ассоциативный контейнер hash_map<Key, Data, HashFcn, Equal, Alloc> который будет ассоциировать B>объект типа Key с объектом типа Data. Интерфейс этого контейнера должен быть аналогичным к STL map, B>только оператор < должен быть заменен оператором ==
B>я уже не знаю за что взятся.
Скопировать из какого-нибудь STL Только избавиться от ассоциативных контейнеров, заменив там логарифмический поиск линейным.
B>а что если его реализовать класами.
Здравствуйте, eBit, Вы писали:
B>Необходимо разработать ассоциативный контейнер hash_map<Key, Data, HashFcn, Equal, Alloc> который будет ассоциировать B>объект типа Key с объектом типа Data. Интерфейс этого контейнера должен быть аналогичным к STL map,
B>только оператор < должен быть заменен оператором ==
Тогда он не будет аналогичен STL map.
STL map для сравнения использует отношение эквивалентности по отношению к порядку сортировки:
два элемента считаются эквивалентными по отношению к порядку сортировки, используемому ассоциативным контейнером, если ни один из них не предшествует другому в данном порядке сортировке. Если перевести на C++, то объекты x и y эквиваленты, если выражение !(x F y) && !(y F y) истоно, где F — функция сравнения. Обычно в качестве данной функции используется <. Но никогда нельзя использовать ==!!! Вот почему: !(x == x) && !(x == x) = !(true) && !(true) = false && false = false !!! .. То есть, если в качестве функции сравнения использовать ==, имеем что объект не эквивалентен сам себе .
B>я уже не знаю за что взятся.
Возьми готовую реализацию — их море
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали:
MN>Возьми готовую реализацию — их море
у меня была идея видрать с STLPort, но посмотрев в код, а там ище всего много подключаетса.
а на счет реализовать класами:
так взять написать "список <ключ ,значение>", hash_table, и уже сам hash_map.
на даный момент работает такой вариант. есть некоторые глюки. но зато свое
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, eBit, Вы писали:
e>> Необходимо разработать ассоциативный контейнер hash_map<Key, Data, e>> HashFcn, Equal, Alloc>
AD>В STLPort есть такая вещь как std::hash_map, её и используй
тогда мне приедетса с кодом программы ище и STLPort передавать.
Здравствуйте, eBit, Вы писали:
B>Здравствуйте, ArtDenis, Вы писали:
AD>>Здравствуйте, eBit, Вы писали:
e>>> Необходимо разработать ассоциативный контейнер hash_map<Key, Data, e>>> HashFcn, Equal, Alloc>
AD>>В STLPort есть такая вещь как std::hash_map, её и используй
B>тогда мне приедетса с кодом программы ище и STLPort передавать.
А что в этом плохого?
Или ты всерьез полагаешь, что твоя реализация будет лучше?
Здравствуйте, Кодт, Вы писали:
К>Скопировать из какого-нибудь STL Только избавиться от ассоциативных контейнеров, заменив там логарифмический поиск линейным.
Ага, написать эмулятор красно-чёрного дерева, который будет отображать "виртуальное" дерево на реальную хеш-таблицу
Здравствуйте, eBit, Вы писали:
B>я уже знаю что в 7 есть, но прикол в том, что у меня задание написать на 6.
B>на счет STLPort, реально с него взять сам hash_map не используя весь STLPort?? (точнее что бы его не ставить всего)
В принципе реально, если выкачаешь все потребные ему файлы, хотя я этим не занимался...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, eBit, Вы писали:
AD>>В STLPort есть такая вещь как std::hash_map, её и используй B>тогда мне приедетса с кодом программы ище и STLPort передавать.
А в STL'е для MSVC2003 уже есть свой hach_map и можно не заморачиваться
Здравствуйте, ArtDenis, Вы писали:
К>>Скопировать из какого-нибудь STL Только избавиться от ассоциативных контейнеров, заменив там логарифмический поиск линейным.
AD>Ага, написать эмулятор красно-чёрного дерева, который будет отображать "виртуальное" дерево на реальную хеш-таблицу
Нет. КЧД, будучи ДДП (двоичным деревом поиска), требует введения строгого порядка.
А по заданию — дано только отношение равенства.
Для линейного поиска необходим и достаточен любой линейный контейнер. В данном случае лучше всего — два списка.
Если над хэшами можно ввести строгий порядок — то получим список и мап, или мап списков.
Если пространство хэшей невелико и отображается на сплошной отрезок целых чисел — то вместо мапа можно использовать вектор.
Тебе потребуется ещё тип хеш-значений. Может быть, его можно вывести из типа HashFcn — но не факт: в общем случае это наложит неоправданные ограничения на параметры шаблона.
Далее, существует ли строгий порядок над хеш-значениями — или тоже только равенство?
Заданы ли эти отношения операторами < и == соответственно?
STL-ные аллокаторы уже параметризованы; тебе потребуется как минимум два аллокатора, причём параметризованные внутренними типами.
Поэтому, наверное, можно потребовать, чтобы Alloc был моделью обобщённого аллокатора (с шаблонными членами), а не STL-аллокатора.
Здравствуйте, Кодт, Вы писали:
К>Тебе потребуется ещё тип хеш-значений. Может быть, его можно вывести из типа HashFcn — но не факт: в общем случае это наложит неоправданные ограничения на параметры шаблона.
тип хеш-значения будет целое число, я имею опредиленый тип даних для сохранения, это будет string, ключ я тоже беру string.
К>Далее, существует ли строгий порядок над хеш-значениями — или тоже только равенство? К>Заданы ли эти отношения операторами < и == соответственно?
я даже не знаю, что за оператор мне нужно заменить.
В шаблонах я не силен, только расбираюсь. И литературы мало по них я нахожу где на пальцах все расказано (на руском).
Уж немало всего разобрал, но как много ище осталось.
Здравствуйте, eBit, Вы писали:
К>>Тебе потребуется ещё тип хеш-значений. Может быть, его можно вывести из типа HashFcn — но не факт: в общем случае это наложит неоправданные ограничения на параметры шаблона.
B>тип хеш-значения будет целое число, я имею опредиленый тип даних для сохранения, это будет string, ключ я тоже беру string.
К>>Далее, существует ли строгий порядок над хеш-значениями — или тоже только равенство? К>>Заданы ли эти отношения операторами < и == соответственно?
B>я даже не знаю, что за оператор мне нужно заменить.
Для string определёны операторы < и ==, чем они тебе не понравились?
B>В шаблонах я не силен, только расбираюсь. И литературы мало по них я нахожу где на пальцах все расказано (на руском). B>Уж немало всего разобрал, но как много ище осталось.
Кстати, о хеше.
Пространство целых чисел имеет мощность INT_MAX. Поэтому, при хорошей хеш-функции, ты будешь иметь коллизии с очень маленькой вероятностью. Поэтому делать двухступенчатый контейнер будет столь же накладно, как и одноступенчатый. Всё равно подребуется двоичный поиск, всё равно это будет КЧ дерево...
Максимальный выигрыш от хеша можно достичь, когда мощность пространства хешей — примерно квадратный корень от ожидаемой мощности множества ключей.
Заодно я придумал обходное решение: в качестве ключа использовать не строку, а строку+хеш.
struct hashstring
{
string str;
int hash;
hashstring() : str(), hash(0) {}
hashstring(const string& s) : str(s), hash(hashfunc(s)) {}
};
bool operator < (const hashstring& l, const hashstring& r)
{
if(l.hash < r.hash) return true;
if(r.hash < l.hash) return false;
// хеши совпали - проверим строкиif(l.str < r.str) return true;
return false; // l >= r
}
// остальные операторы определяются через оператор <
// впрочем, для map достаточно только его
/////////
map<hashstring, my_value_type> my_table;
То есть мы ничего не меняем в структурах данных (как был мап на КЧД, так и остался), но существенно ускоряем поиск: все сравнения проходят в первую очередь по хешу (быстро), и только затем, в случае коллизии, — по строке (медленно).
Побочный эффект — кажущаяся неотсортированность такого мапа.
Точнее, он отсортирован по hashstring, чей порядок существенно отличается от алфавитного.
При желании — можешь обобщить этот приём, создав класс, который содержит внутри такой мап.
К>Максимальный выигрыш от хеша можно достичь, когда мощность пространства хешей — примерно квадратный корень от ожидаемой мощности множества ключей.
Последнее, наверно, требует некоторых комментариев.
Немного математики:
Мощность пространства хешей h, мощность множества ключей k.
Среднее количество ключей с одинаковым хешем c = max(1, k/h).
Время поиска T = Th(h) * Tk(c), где Th — поиск хешей, Tk — поиск ключей в множестве коллизий данного хеша.
Если таблица хешей имеет произвольный доступ (что возможно при h гораздо меньшем чем 2^32),
O(T) = O(1) * O(Tk(c)) = O(Tk(k/h)).
Тут очевидно, что чем больше h, тем лучше. И всё упирается в цену памяти.
Если же поиск логарифмический (при большом h)
O(T) = O(log h) * O(Tk(k/h)) = O(log(h) * Tk(k/h)).
Допустим, поиск по коллизиям линейный, O(Tk(k/h)) == O(k/h)
O(T) = O(log(h)/h * k)
Снова понятно, что чем больше h, тем лучше: log(h)/h убывает.
Наконец, если поиск по коллизиям логарифмический,
O(T) = O(log(h) * log(k/h)) = O(log(h) * (log(k) — log(h)))
то минимум будет при
x = log(h)
y = log(k)
f = x*(y-x) = xy — x^2
f' = y — 2x
f'==0, x = y/2
log(h) = log(k)/2 = log(k^(1/2))
h = sqrt(k)
Здравствуйте, eBit, Вы писали:
B>... STL map, только оператор < должен быть заменен оператором ==
B>а как это понять, в чем суть, скажите ...
Какой мексиканский негодяй тебе дал такое задание — вот у него и спроси, что он имел в виду.
STL-ные ассоциативные контейнеры (set, map, multiset, multimap) требуют, чтобы над элементами существовало отношение строгого порядка.
Как, впрочем, любые сортированные контейнеры. Потому что только на сортированном контейнере можно осуществлять двоичный поиск.
А отношение равенства не позволяет однозначно сортировать. Это означает, что поиск будет не двоичный, а линейный.
Как сделать хеш-контейнер с линейным поиском из STL-ных деталек — я уже выше показывал.
К>Мощность пространства хешей h, мощность множества ключей k. К>Среднее количество ключей с одинаковым хешем c = max(1, k/h).
К>Время поиска T = Th(h) * Tk(c), где Th — поиск хешей, Tk — поиск ключей в множестве коллизий данного хеша.
Дурня свалял.
Конечно же, T = Th + Tk.
Поэтому все последующие формулы — прошу считать инсинуациями.
B>В шаблонах я не силен, только расбираюсь. И литературы мало по них я нахожу где на пальцах все B>расказано (на руском). B>Уж немало всего разобрал, но как много ище осталось.
Псмотри вот эту книгу
Здравствуйте, Кодт, Вы писали:
К> ... К> А отношение равенства не позволяет однозначно сортировать. Это означает, К> что поиск будет не двоичный, а линейный.
Ну, ну...
Вообще-то в хеш-таблице поиск должен осуществлятся почти за константное время (время вычисления хеш-функции). Исключения составляют коллизии, но их можно не учитывать.
Оператор == в хеш-контейнере нужен лишь для того, чтобы диагностировать коллизию.
Здравствуйте, maq, Вы писали:
maq>Мне кажется проще взять из Dinkumware, у него и исходник куда более читабельный имхо.
Ве эти исходники очень большие, читал я их, достали они меня, но зато много интересного нашел.
Самое интересное — мне нужно спмому написать hash_map, а не передрать.
Здравствуйте, eBit, Вы писали:
B>Как вам такой хеш мап B>Скажите свок ФЕ, и что можна добавить, улучшить.
B>
B>template <class _Key, class _Val , class _HashFcn>
B>class hash_map {
B> private:
B> datalist_type _dl; // список всех пар
B> hash_table_type _hl; // хаш таблиця для пар
B> hash_function _hf;
B> int data_capacity; // количество сохраненых елементов
B> public:
B> typedef _Key key_type;
B> typedef _Val value_type;
B> typedef _HashFcn hash_function;
B> typedef size_t hash_type;
B> typedef std::pair<key_type, value_type> data_type;
B> typedef std::list<data_type*> datalist_type; // пары с однаковым хешом
B> typedef std::vector<datalist_type> hash_table_type;
Получается, у тебя есть один список всех элементов (_dl) и ещё отдельно списки коллизий (_hl) ?
Да ещё и совместное владение элементами (хранящимися по указателям).
А нафига, раскрой секрет.
На мой взгляд, удобнее иметь список элементов (отсортированный по хешу) и таблицу итераторов.
Для отдельного, искушённого пользователя можно сделать итерирование по хешам. Но вообще это не имеет смысла.
Вот тебе уже пришлось ввести два разных типа итераторов — один на домене хештаблицы, а другой — на домене списка-помойки.
Кстати, если уж давать имя, то не iterator_list (список итераторов), а list_iterator (итератор списка).
По некоторым соображениям, лучше использовать хеш с размером, равным простому числу. Достигается лучшее перемешивание. (Подробности — у Кнута; я, к сожалению, не вчитывался, а сейчас книги нет).
Очень весело! Мне по ключу выдают список элементов, где я ещё и должен искать этот ключ. Ладно бы эти элементы были логически сгруппированы (и имел бы смысл такой "нечёткий запрос" — как, например, std::multiset/multimap). Так ведь нет — элементы перемешаны! То есть их объединение — исключительная прихоть судьбы в виде хеш-функции.