Алгоритм автозаполнения? - PullRequest
58 голосов
/ 25 мая 2010

Я имею в виду алгоритм, который используется для запроса предложений, когда пользователь вводит поисковый запрос в Google.

Меня в основном интересуют: 1. Наиболее важные результаты (скорее всего, запросы, а не все, что соответствует) 2. Подходим подстроки 3. Нечеткие спички

Я знаю, что вы можете использовать Trie или обобщенное trie для поиска совпадений, но это не будет соответствовать вышеуказанным требованиям ...

Подобные вопросы задавались ранее здесь

Ответы [ 8 ]

55 голосов
/ 15 июля 2011

Для (хех) потрясающих алгоритмов нечеткого / частичного сопоставления строк, посмотрите чертовски крутые алгоритмы:

Они не заменяют попытки, а скорее предотвращают грубый поиск в попытках - что по-прежнему является огромной победой. Далее, вам, вероятно, нужен способ ограничения размера дерева:

  • сохранить список последних / топ N слов, используемых во всем мире;
  • для каждого пользователя, сохраняйте список последних / первых N слов для этого пользователя.

Наконец, вы хотите предотвратить поиск, когда это возможно ...

  • результаты поиска в кэше: если пользователь щелкает результаты поиска, вы можете очень быстро их обработать, а затем асинхронно получить полный частичный / нечеткий поиск.
  • результаты предварительного поиска: если пользователь набрал «appl», он, скорее всего, продолжит с «apple», «apply».
  • данные предварительной выборки: например, веб-приложение может отправлять в браузер меньший набор результатов, достаточно малый, чтобы обеспечить непрерывный поиск в JS.
8 голосов
/ 27 января 2012

Я просто хотел бы сказать ... Хорошее решение этой проблемы будет включать в себя больше, чем троичное дерево поиска. Нграммы и опоясывающий лишай (фразы) необходимы. Ошибки границы слова также должны быть обнаружены. «черт возьми» должно быть «привет» ... и «белые» носки - это «белые носки» - это этапы предварительной обработки. Если вы не обработаете данные должным образом, вы не получите ценные результаты поиска. Тройные деревья поиска являются полезным компонентом для определения того, что является словом, а также для реализации угадывания связанных слов, когда набранное слово не является допустимым словом в индексе.

Алгоритм Google выполняет предложение и исправление фразы. Алгоритм Google также имеет некоторую концепцию контекста ... если первое слово, которое вы ищете, связано с погодой, и вы объединяете их "weatherforcst" против "monsoonfrcst" против "deskfrcst" - я думаю, что за кулисами рейтинг меняется в предложение, основанное на первом встретившемся слове - прогноз и погода - это родственные слова, поэтому прогноз получает высокий рейтинг в предположении "Сделай сам".

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

5 голосов
/ 01 июля 2013

Точный алгоритм Google неизвестен, но, как говорят, работает на основе статистического анализа ввода пользователей. Подход не подходит для большинства случаев. Чаще всего автоматическое завершение выполняется с помощью одного из следующих:

  • Деревья . Индексируя текст с возможностью поиска в древовидной структуре (дерево префиксов, дерево суффиксов, dawg и т. Д.), Можно выполнять очень быстрый поиск за счет хранения памяти. Обход дерева может быть адаптирован для приблизительного соответствия.
  • Разделение по шаблонам . Разбивая текст на токены (нграммы), можно выполнять поиск вхождений в шаблон с использованием простой схемы хеширования.
  • Фильтрация . Найдите набор потенциальных совпадений, а затем примените последовательный алгоритм для проверки каждого кандидата.

Взгляните на полностью , библиотеку автозаполнения Java, которая реализует некоторые из последних концепций.

4 голосов
/ 25 мая 2010

Существуют такие инструменты, как soundex и расстояние Левенштейна , которые можно использовать для поиска нечетких совпадений, которые находятся в определенном диапазоне.

Soundex находит слова, которые звучат одинаково, а расстояние Левенштейна находит слова, которые находятся на определенном расстоянии редактирования от другого слова.

3 голосов
/ 19 июля 2010

Взгляните на Алгоритм потрясающей панели Firefox

Предложение Google полезно, потому что оно учитывает миллионы популярных запросов + ваши прошлые связанные запросы.

У него нет хорошего алгоритма завершения / пользовательского интерфейса:

  1. Не делает подстрок
  2. Похоже, относительно простой алгоритм префикса границы слова.
    Например: Попробуйте tomcat tut -> правильно предложить "tomcat tutorial". Теперь попробуйте tomcat rial -> нет предложений) -:
  3. Не поддерживает "ты имел в виду?" - как в результатах поиска Google.
2 голосов
/ 21 февраля 2014

Для подстрок и нечетких совпадений алгоритм расстояния Левенштейна работал для меня довольно хорошо. Хотя я признаю, что он не так совершенен, как отраслевые реализации автозаполнения / предложения. Я думаю, что и Google, и Intellisense от Microsoft делают лучшую работу, потому что они усовершенствовали этот базовый алгоритм, чтобы взвесить тот тип операций редактирования, который требуется для соответствия разнородным строкам. Например. транспонирование двух символов должно, вероятно, считаться только 1 операцией, а не 2 (вставка и удаление).

Но даже в этом случае я нахожу это достаточно близким. Вот его реализация в C # ...

// This is the traditional Levenshtein Distance algorithem, though I've tweaked it to make
// it more like Google's autocomplete/suggest.  It returns the number of operations 
// (insert/delete/substitute) required to change one string into another, with the 
// expectation that userTyped is only a partial version of fullEntry.
// Gives us a measurement of how similar the two strings are.
public static int EditDistance(string userTyped, string fullEntry)
{
    if (userTyped.Length == 0) // all entries are assumed to be fully legit possibilities 
        return 0; // at this point, because the user hasn't typed anything.

    var inx = fullEntry.IndexOf(userTyped[0]);
    if (inx < 0) // If the 1st character doesn't exist anywhere in the entry, it's not
        return Int32.MaxValue; // a possible match.

    var lastInx = inx;
    var lastMatchCount = 0;
TryAgain:
    // Is there a better starting point?
    var len = fullEntry.Length - inx;
    var matchCount = 1;
    var k = 1;
    for (; k < len; k++)
    {
        if (k == userTyped.Length || userTyped[k] != fullEntry[k + inx])
        {
            if (matchCount > lastMatchCount)
            {
                lastMatchCount = matchCount;
                lastInx = inx;
            }
            inx = fullEntry.IndexOf(userTyped[0], inx + 1);
            matchCount = 0;
            if (inx > 0)
                goto TryAgain;
            else
                break;
        }
        else
            matchCount++;
    }
    if (k == len && matchCount > lastMatchCount)
        lastInx = inx;

    if (lastInx > 0)
        fullEntry = fullEntry.Substring(lastInx); // Jump to 1st character match, ignoring previous values 

    // The start of the Levenshtein Distance algorithem.
    var m = userTyped.Length;
    var n = Math.Min(m, fullEntry.Length);

    int[,] d = new int[m + 1, n + 1]; // "distance" - meaning number of operations.

    for (var i = 0; i <= m; i++)
        d[i, 0] = i; // the distance of any first string to an empty second string
    for (var j = 0; j <= n; j++)
        d[0, j] = j; // the distance of any second string to an empty first string

    for (var j = 1; j <= n; j++)
        for (var i = 1; i <= m; i++)
            if (userTyped[i - 1] == fullEntry[j - 1])
                d[i, j] = d[i - 1, j - 1];       // no operation required
            else
                d[i, j] = Math.Min
                           (
                             d[i - 1, j] + 1,  // a deletion
                             Math.Min(
                             d[i, j - 1] + 1,  // an insertion
                             d[i - 1, j - 1] + 1 // a substitution
                             )
                           );

    return d[m, n];
}
1 голос
/ 25 августа 2016

Если вы ищете общий дизайн для проблемы, попробуйте прочитать содержание на https://www.interviewbit.com/problems/search-typeahead/.

Они начинают с построения автозаполнения посредством наивного подхода использования дерева и затем строят его. Они также объясняют методы оптимизации, такие как выборка и автономные обновления, для удовлетворения конкретных случаев использования.

Чтобы сохранить масштабируемость решения, вам необходимо разумно осилить свои данные.

0 голосов
/ 16 июля 2010

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

Я мог видеть, что функциональность проявлялась в дереве, в котором каждый лист имел поле, отражающее частоту поиска соответствующего слова.

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

Структура данных (и, следовательно, алгоритм), которые использует Google, вероятно, значительно сложнее, потенциально принимая во внимание большое количество других факторов, таких как частота поиска по вашей конкретной учетной записи (и время суток ... и погода). .. сезон ... и лунная фаза ... и ...). Тем не менее, я считаю, что базовая структура данных Trie может быть расширена до любого вида специализированных поисковых предпочтений путем включения дополнительных полей для каждого из узлов и использования этих полей в методе поискового запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...