Тестовое задание на стажировку в Яндекс.Крипта
Программа тестировалась на Linux Mint 18 c GCC 5.4.0.
Для сборки есть Makefile
- make - собирает программу в файл main
- make debug - то же самое с флагом -g и дополнительным выводом слов в terms.txt, сниппетов в snippets.txt.
- make clean - очистить папку от *.o и main.
Алгоритм работает следующим образом:
- Файл полностью считывается в одну строку.
- Текст парсится классом Parser, тот наполняет следующие коллекции:
- SnippetStorage - вектор объектов Snippet. Snippet - строка сниппета предпосчитанными TF, TF-IDF для всех слов в нём (строка SnippetTermFrequency). Строка сниппета состоит из предложений - строк оканчивающихся на [.?!]. В сниппете лежит не менее 15 слов, параметр можно настраивать в main.cpp.
- TermDatabase - unordered_map где ключи слова, значения - объекты TermData. TermData содержит в себе все индексы всех сниппетов, в которых содержится данное слово, отсортированные по убыванию TF-IDF этого слова в данном сниппете.
TF вычисляется как доля данного слова в сниппете, IDF - как обратная доля данного слова во всём документе, TF-IDF - их произведение.
Слова - строки, не содержащие ASCII знаков пунктуации и whitspace. Английские ASCII символы приводятся к нижнему регистру, какие-либо иные варианты кодировок не учитываются и используются "как есть".
- Запросы из stdin передаются в SnippetEngine
- Запрос разбивается на неповторяющиеся слова, для каждого из них извлекаются TermData.
- У каждого из TermData берутся итераторы на отсортированные сниппеты, по ним вычисляется bestPossibleScore как сумма TF-IDF с начальных итераторов.
- Итераторы складываются в termRank - отсортированный map по TF-IDF сниппета, на который смотрит итератор
- Далее идёт цикл:
- Выбирается итератор с лучшим TF-IDF
- Если сниппет в итераторе уже рассматривался, переходим на шаг 4
- Для сниппета вычисляется оценка как сумма TF-IDF по всем словам запроса, лучший сниппет обновляется.
- Итератор пропускает все сниппеты, которые были ранее рассмотрены.
- Обновлются termRank и bestPossibleScore.
- Если bestScore >= bestPossibleScore, цикл завершается.
Есть опция useMostMatches, с которой рассматриваются только сниппеты с максимальным количеством совпадений со словами из запроса, т.е. программа будет стараться доставать сниппеты, имеющие как можно больше слов из запроса. Это улучшает качество сниппетов, но и увеличивает время работы с 1-10 мкс до 100-1000 мкс. (на файле 1МБ). Параметр также настраивается в main.cpp, по умолчанию включён.