Допустим у нас есть некая таблица(а.к.а. словарь) состоящая из N ключевых слов[1].
Нам нужно написать функцию типа:
int search(const char* some_word);
которая быстро возвращает либо число из диапазона [1..N] если some_word в таблице или 0 если такого слова там нет.
Задача построения такой функции решаема несколькими вариантами, один из них например
построить т.н. minimal perfect hash function с помощью например gperf (но это возможно не на каждом наборе).
Второй вариант — воспользоваться предлагаемой имплементацией ternary tree [2]:
Класс ternary_tree кроме двух очевидных и полезных методов:
neighbour_search позволяет находить "похожие" словаю. Мера похожести задается параметром d — HAMMING DISTANCE [3].
например neighbour_search("twa",2) найдет два слова "twa" и "twas"
Замечания по имплементации:
1) Я использую свой array который может быть легко заменен на std::vector
2) Выбранный способ аллокации nodes (элементы в масиве) помимо всего прочего позволяет
задавать compiled static node tables — т.е. если у вас есть статическая таблица keywords то в принципе её можно скомпилировать и разместить в коде как static. Т.е. для проведения search не надо будет делать inserts при инициализации.
Алгоритм известен своей практически идеальной производительностью.
Во всяком случае теоретически лучше чем hash_table's
Здравствуйте, korzhik, Вы писали:
K>Здравствуйте, c-smile, Вы писали:
CS>>Второй вариант — воспользоваться предлагаемой имплементацией ternary tree [2]:
K>спасибо, очень полезная вещь. K>правда я пока не тестил, но думаю от вас всё идёт со знаком качества.
Спасибо
K>кстати имплементацию в boost::spirit'е не смотрели? K>если интересно то путь такой boost\spirit\symbols\impl\tst.ipp
Спасибо за ссылку, глянул.
На вскидку что бросается в глаза сразу — это аллокция nodes в heap что в общем-то не эффективно для массива структур имеющих одинаковый размер экземпляров. Плюс расход памяти.
У меня например вместо указателя на node хранится его индекс который в большинстве случав может быть short int а не pointer как в boost/spirit.
Например для моего тестового примера таблица имен цветов в HTML (http://www.w3schools.com/html/html_colornames.asp) примерно 140 элементов.
Количество nodes примерно 1030. (Для справки общее количество символов во всех словах таблицы около 1200).Т.е. при аллокации на хипе несколько затратно/неэффективно получается.
CS>Допустим у нас есть некая таблица(а.к.а. словарь) состоящая из N ключевых слов[1]. CS>Нам нужно написать функцию типа: CS>
CS> int search(const char* some_word);
CS>
CS>которая быстро возвращает либо число из диапазона [1..N] если some_word в таблице или 0 если такого слова там нет.
У меня честно говоря не было времени вникать в сорсы и теорию, можно ли воспроизвести слово "быстро" в математических терминах типа О(х)?
И второй вопрос: может ли такое случиться, что при определенном наборе слов тернарное дерево вырождается, и какие действия предлагаются в этом случае для его балансировки?
Serge.
Hасколько проще была бы жизнь, если бы она была в исходниках.
Здравствуйте, Sergeem, Вы писали:
CS>>которая быстро возвращает либо число из диапазона [1..N] если some_word в таблице или 0 если такого слова там нет.
S>У меня честно говоря не было времени вникать в сорсы и теорию, можно ли воспроизвести слово "быстро" в математических терминах типа О(х)?
Насколько я понимаю поиск строки длины k в дереве с n элементами займет O(log n+k) шагов в худшем случае.
Здравствуйте, Sergeem, Вы писали:
S>У меня честно говоря не было времени вникать в сорсы и теорию, можно ли воспроизвести слово "быстро" в математических терминах типа О(х)? S>И второй вопрос: может ли такое случиться, что при определенном наборе слов тернарное дерево вырождается, и какие действия предлагаются в этом случае для его балансировки?
Time Complexity
In a balanced ternary search tree with n nodes where all the keywords
of the nodes are k characters long, we want to search a k character long input string. The total number of character-to-character comparisons is at most
lg n + k.
For a balanced binary search tree of n-nodes and k character long keywords, the total number of character-to-character comparisons is at most
k * lg n
which is substantially larger than lg n + k for large n.
Ternary tree не требует балансировки.
Оно всегда в "хорошем состянии"
In what order should you insert the nodes into a tree? No matter in what order you insert the nodes, you end up with the same digital search trie -- the data structure is totally insensitive to insertion order.
Binary search trees are at the opposite end of the spectrum: If you insert the nodes in a good order (middle element first), you end up with a balanced tree. If you insert the nodes in sorted order, the result is a long skinny tree that is very costly to build and search. Fortunately, if you insert the nodes in random order, a binary search tree is usually close to balanced.
Тем не менее скорость работы функции insert зависит от порядка следования подаваемых слов, search — нет.
Справедливости ради надо отметить что hash-table как механизм тоже вельми эффеткивен.
При хорошем раскладе (а.к.а. неловленный мизер) количество character-to-character comparisons
может быть вообще
2 * k
(я приравниваю сложность обработки одного символа при вычислении хэш функции к одному сравнению, что в общем не так, но близко).
При плохом раскладе (все слова попали в один collision chain) количество сравнений
Здравствуйте, c-smile, Вы писали:
CS>Ternary tree не требует балансировки. CS>Оно всегда в "хорошем состянии"
Скорее всего это было мое оптимистичное заявление.
Потому как вот:
The input order of data cells into a ternary search trie is still important to create balanced tree, but less important than in a binary search tree.
According to many authors ([Mehlhorn79] and [Bentley and Saxe79]), by choosing the true median of a set of data-cells as the starting node, we can create a well-balanced ternary tree without the randomizing proces
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, c-smile, Вы писали:
CS>>Ternary tree не требует балансировки. CS>>Оно всегда в "хорошем состянии"
CS>Скорее всего это было мое оптимистичное заявление.
CS>Потому как вот: CS>
CS>The input order of data cells into a ternary search trie is still important to create balanced tree, but less important than in a binary search tree.
CS>According to many authors ([Mehlhorn79] and [Bentley and Saxe79]), by choosing the true median of a set of data-cells as the starting node, we can create a well-balanced ternary tree without the randomizing proces
Здравствуйте, Sergeem, Вы писали:
S>Здравствуйте, c-smile, Вы писали:
CS>>Допустим у нас есть некая таблица(а.к.а. словарь) состоящая из N ключевых слов[1]. CS>>Нам нужно написать функцию типа: CS>>
CS>> int search(const char* some_word);
CS>>
CS>>которая быстро возвращает либо число из диапазона [1..N] если some_word в таблице или 0 если такого слова там нет.
S>У меня честно говоря не было времени вникать в сорсы и теорию, можно ли воспроизвести слово "быстро" в математических терминах типа О(х)? S>И второй вопрос: может ли такое случиться, что при определенном наборе слов тернарное дерево вырождается, и какие действия предлагаются в этом случае для его балансировки?
По многим вопросам, и в частности по вопросу тернарного дерева(кстати, на днях сам реализовывал, поздно заглянул сюда), ИМХО, лучше обратиться к Седжвику. Там все достаточно подробно и авторитетно расписано, в том числе есть ответы и на ваши вопросы.
CS>Допустим у нас есть некая таблица(а.к.а. словарь) состоящая из N ключевых слов[1]. CS>Нам нужно написать функцию типа: CS>
CS> int search(const char* some_word);
CS>
CS>которая быстро возвращает либо число из диапазона [1..N] если some_word в таблице или 0 если такого слова там нет.
CS>Задача построения такой функции решаема несколькими вариантами, один из них например CS>построить т.н. minimal perfect hash function с помощью например gperf (но это возможно не на каждом наборе).
[skiped] CS>
Решил испытать ваше решение,
После объявления
tool::ternary_tree<char> TST;
при попытке компиляции вываливается:
d:\Prog. NET\Solution1\Dict_Search\main.cpp(230) : error C2440: '=' : cannot convert from 'const tool::ternary_tree<CHAR>::node' to 'tool::ternary_tree<CHAR>::node_index'
node_index insert(node_index nid, const CHAR *s)
{
if (nid >= total_nodes())
{
node n; n.splitchar = *s;
nodes.push(n);
nid = nodes.last_index(); // здесь ошибка
}
Может быть подскажете, что не так делаю?
И еще вопрос параллелльно : на сколько ваш array по быстродействию выигрывает у vector? Думаю, измерения проводились...
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Dmitry521, Вы писали:
CS>>>
size_t ternary_tree::neighbour_search(const char *s, int d, array<dword>& result_set);
CS>>>
Посмотрел как работает эта функция.
Мне кажется она неверно считает HAMMING DISTANCE.
например я ввел пять слов
woman, man, many, wman, mwan
искал слово man.
результаты
d = 1 man, many Я считаю должно было быть — man, many, mwan, wman
d = 2 man, many Я считаю должно было быть — man, many, mwan, wman, woman
d = 3 man, many, mwan
d = 4 man, many, mwan, wman
d = 5 man, many, mwan, wman, woman
Наблюдается очень сильная зависимость от начала слова.
Здравствуйте, uw, Вы писали:
uw>Здравствуйте, Dr.Gigabit, Вы писали:
uw>Вот тот же код, слегка переделанный под std::vector, вроде работает.
vector<dword> resultSet;
int i;
tool::ternary_tree<char> TST;
TST.insert("Andy");
TST.insert("Andyx");
i = TST.neighbour_search("Andy",1, resultSet); // здесь i = 2 как положеноfor(vector<dword>::iterator it = resultSet.begin(); it != resultSet.end(); it++)
cout << *it << endl; //здесь что-то не то
Что не так делаю с resultSet. По идее должны выводиться найденные слова