2012-01-13 4 views
12

Проблема: большой статический список строк предоставляется как A, длинная строка предоставляется как B, строки в A все очень короткие (список ключевых слов), я хочу проверить если каждая строка в A является подстрокой B и получает их.Высокопроизводительная массовая короткая строка поиска в Python

Теперь я использую простой цикл, как:

result = [] 
for word in A: 
    if word in B: 
     result.append(word) 

Но это безумие медленно, когда А содержит ~ 500000 или более пунктов.

Есть ли библиотека или алгоритм, который подходит для этой проблемы? Я старался изо всех сил искать, но не повезло.

Спасибо!

+0

просто теория - что, если вы пытаетесь с помощью 'B.find (слово)' вместо 'если слово в b'? Я считаю, что 'in' является быстрым, если подстрока действительно находится в' B', но она будет намного медленнее, если это не подстрока. 'find' может быть быстрее. – birryree

+0

@birryree Спасибо за комментарий, но в моих тестах с использованием 'B.find (word)' вместо 'word in B' не приложил никаких усилий в производительности :( –

ответ

14

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

Взгляните на алгоритм Aho-Corasick. Ваша постановка проблемы - это парафраз проблемы, которую решает этот алгоритм.

Также изучите работу Николая Лехуэна с его пакетом PyTST.

Есть также ссылки в родственном сообщении переполнения стеки, которые упоминают другие алгоритмы, такие как Рабин-Карп: Algorithm for linear pattern matching?

+0

+1 - это ответ Я думал о [trie] (http://en.wikipedia.org/wiki/Trie) -общем но это еще лучше. – senderle

+0

Большое вам спасибо, что у меня есть все, чтобы работать отлично, вот мой результат теста: '2012-01-13 11: 48: 07.632212 Импорт тестовых примеров'' 2012-01-13 11: 48: 17.191975 Сканирование с использованием in '' 2012-01-13 11: 48: 47.750070 Сканирование завершено '2012-01-13 11: 48: 47.752614 TSTing'' 2012-01-13 11: 48: 56.780503 Сканирование с использованием tst'' 2012-01-13 11: 48: 56.798033 Сканирование завершено. –

1

Я не знаю, если это будет какой-нибудь быстрее, но это намного больше вещий:

result = [x for x in A if x in B] 
+3

Да, это больше питонов, но быстрее не произойдет: ( –

+0

(+1) Я не думаю, что есть лучшее решение. (За исключением использования ориентированного на производительность языка, такого как C). – FakeRainBrigand

1

Упаковать все отдельные слова B в новый список, состоящий из исходной строки расщепленного ' '. Затем для каждого элемента в B проверьте соответствие членства каждому элементу A. Если вы найдете один (или более), удалите его/их с A и закройте, как только A пуст.

Похоже, что ваш подход будет пробиваться через 500 000 кандидатов без установки отказа.

+0

Извините, я не дал понять, что строки находятся на китайском языке, поэтому слова не разделенные пробелами. Мне нужно будет сделать гораздо больше работы, чтобы узнать «все отдельные слова« B ». –

+1

@FelixYan мой последний комментарий должен быть моим единственным полезным советом для вас: найдите способ похудеть список кандидатов по мере того, как вы расчесываете 'B'. Еще один член во внешнем цикле' for' с использованием 'A' ускорит ваше время поиска, как бы вы ни занимались. – Droogans

2

В зависимости от того, как долго вашей длинной строки, оно может быть стоит сделать что-то вроде этого:

ls = 'my long string of stuff' 
#Generate all possible substrings of ls, keeping only uniques 
x = set([ls[p:y] for p in range(0, len(ls)+1) for y in range(p+1, len(ls)+1)]) 

result = [] 
for word in A: 
    if word in x: 
     result.append(word) 

Очевидно, что если ваша длинная строка очень и очень длинная, это также становится слишком медленным, но оно должно быть быстрее для любой строки под несколькими сотнями символов.

+1

ОП указал здесь очень важную деталь: он разбирает иероглифы. Поэтому 'ls = 'mylongstringofstuff'', и не будет очень полезно сопоставлять набор между комбинациями индексов' ls'. – Droogans

+0

@Droogans Я отправил это, прежде чем добавил, но я все еще не вижу проблемы. Пусть 'ls = '解析 す る た め に い く つ か の 文字' как случайный пример (японских) символов - выше все еще (почти) работает (на самом деле есть небольшая ошибка в том, что я написал, что я пытаюсь исправить , но я думаю, что идея все еще прекрасна). Изменить: ошибка должна быть исправлена. – Yuushi

+0

Прошу прощения. Если вы компилируете свои примеры с использованием символов кандзи без пробелов и получаете полезные результаты, я не должен понимать ваш код (или проблему) так четко, как должен. – Droogans

1

Предположим, есть все ключевые слова одинаковой длины (позже вы можете расширить этот Algo для различных длин)

Я мог бы предложить следующий:

  1. предвычислять некоторые хэш для каждого ключевого слова (например, исключающего хэш):

    hash256 = reduce(int.__xor__, map(ord, keyword)) 
    
  2. создать словарь, где ключ является хэш и список значений из соответствующих ключевых слов

  3. сохранить длину ключевого слова

    curr_keyword = [] 
    for x in B: 
        if len(curr_keyword) == keyword_length: 
        hash256 = reduce(int.__xor__, map(ord, curr_keyword)) 
        if hash256 in dictionary_of_hashed: 
         #search in list 
    
        curr_keyword.append(x) 
        curr_keyword = curr_keyword[1:] 
    

Что-то вроде этого

Смежные вопросы