Проверка, содержит ли строка английское предложение

на данный момент я решил взять словарь и повторить все это. Каждый раз, когда я вижу новую строку, я делаю строку, содержащую от этой новой строки до следующей новой строки, затем я делаю строку.найдите (), чтобы увидеть, есть ли это английское слово где-то там. Это занимает очень много времени, каждое слово занимает около 1/2-1/4 секунды, чтобы проверить.

он работает отлично, но мне нужно проверить тысячи слов в секунду. Я могу запустить несколько окон, что не влияет на скорость (Многопоточность), но он по-прежнему проверяет только 10 в секунду. (Мне нужны тысячи)

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

строки, которые я проверяю, будут выглядеть так:

"hithisisastringthatmustbechecked"

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

Я не могу проверить невозможность компиляции букв, потому что эта строка будет выброшена из-за "tm", между "thatmust".

5 ответов


есть много стратегий, чтобы сделать это быстро.

Идея 1

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

таким образом, массив выглядит следующим образом:

a - substr[0] = "astringthatmustbechecked"
b - substr[1] = "bechecked"
c - substr[2] = "checked"
d - substr[3] = "d"
e - substr[4] = "echecked"
f - substr[5] = null // since there is no 'f' in it
... and so forth

затем, для каждого слова в словарь, поиск в элементе массива, указанном его первой буквой. Это ограничивает количество вещей, которые нужно обыскать. Кроме того, вы никогда не можете найти слово, начинающееся с, скажем, "r", нигде до первого " r " в строке. И некоторые слова даже не будут искать, если письма там вообще нет.

Идея 2

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

так что у вас есть это в массиве:

a - substr[0] = "astringthatmustbechecked"

но если самое длинное слово в списке-5 букв, нет необходимости держать больше, чем:

a - substr[0] = "astri"

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

e - substr[4] = "echecked"

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

Идея 3

это не имеет ничего общего с 1 и 2. Это идея, которую вы могли бы использовать вместо этого.

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

предположим, что это слова в словаре:

arun
bob
bill
billy
body
jose

создайте такую связанную структуру. (Его двоичное дерево, действительно, представлено таким образом, что я могу объяснить, как его использовать.)

a -> r -> u -> n -> *
|
b -> i -> l -> l -> *
|    |              |
|    o -> b -> *    y -> *
|         |
|         d -> y -> *
|
j -> o -> s -> e -> *

стрелки обозначают букву, которая должна следовать за другой буквой. Так что " Р "должно быть после" а", иначе оно не может совпадать.

строки, идущие вниз, обозначают опцию. У вас есть" a или b или j "возможные буквы, а затем" i или o "возможные буквы после"b".

регулярное выражение выглядит примерно так: /(arun)|(b (ill (y+))|(o (b|dy))) / (jose) / (хотя я мог бы подсунули парень). Это дает суть создания его как регулярного выражения.

как только вы построите эту структуру, вы примените ее к своей строке, начиная с первого столбца. Попробуйте запустить матч, проверив альтернативы, и если один соответствует, более вперед ориентировочно и попробуйте букву после стрелки и ее альтернатив. Если вы достигнете звезды / звездочки, она совпадет. Если у вас закончились альтернативы, в том числе возврат, вы переходите к следующему столбцу.

это много работа, но иногда может пригодиться.

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

подумайте о каждом наборе параметров вертикальной панели, являющихся switch оператор против определенного столбца символов и каждой стрелки, превращающейся в вложенность. Если есть только один вариант, вам не нужно полное switch заявление, просто Ан if.

это было быстрое сопоставление символов и очень удобно по какой-то причине, которая ускользает от меня сегодня.


вы можете ускорить поиск, используя алгоритм Кнута–Морриса–Пратта (KMP).

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


как о Bloom Filter?

фильтр Bloom, задуманный Бертоном Говардом Блумом в 1970 году пространственно-эффективная вероятностная структура данных, используемая для тестирования является ли элемент членом набора. Ложные положительные матчи возможно, но ложных негативов нет; т. е. запрос возвращает либо "внутри набора (может быть неправильно) "или"определенно не в наборе". Элементы могут добавить в набор, но не удалить (хотя это можно решить с фильтр" подсчет"). Чем больше элементов добавляется к набор, тем больше вероятность ложных срабатываний.

подход может работать следующим образом: вы создаете набор слов, которые хотите проверить (это делается только один раз), а затем вы можете быстро запустить проверку "in/not-in" для каждой подстроки. Если результат "не-в", вы можете продолжить (фильтры Bloom не дают ложных негативов). Если результат "в", ВЫ затем запустите более сложную проверку для подтверждения (фильтры Bloom могут давать ложные срабатывания).

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


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

единственная разница для обучения на предложениях заключается в том, что ваши таблицы вероятностей будут немного больше. По моему опыту, современный настольный компьютер имеет более чем достаточно ОЗУ для обработки матриц Маркова, если вы не тренируетесь на всей библиотеке Конгресса (что необязательно - даже 5 или около того книг разных авторов должно быть достаточно для очень точной классификации).

поскольку ваши предложения объединены без четких границ слов, это немного сложно, но хорошая новость заключается в том, что модель Маркова не заботится о словах, просто о том, что следует за чем. Таким образом, вы можете заставить его игнорировать пробелы, сначала очистив все пробелы от ваших данных обучения. Если вы собирались использовать "Алису в Стране Чудес" в качестве учебного текста, первый абзац, возможно, будет выглядеть так:--3-->

alicewasbeginningtogetverytiredofsittingbyhersisteronthebankandofhavingnothingtodoonceortwiceshehadpeepedintothebookhersisterwasreadingbutithadnopicturesorconversationsinitandwhatistheuseofabookthoughtalicewithoutpicturesorconversation

Это выглядит странно, но что касается модели Маркова, это тривиальное отличие от классическая реализация.

Я вижу, что вас беспокоит время: обучение может занять несколько минут (при условии, что вы уже составили тексты "предложений" и "случайных скремблированных строк" золотого стандарта). Вам нужно тренироваться только один раз, вы можете легко сохранить "обученную" модель на диск и повторно использовать ее для последующих запусков, загрузив с диска, что может занять несколько секунд. Выполнение вызова строки займет тривиально небольшое количество умножений с плавающей запятой, чтобы получить вероятность, поэтому после того, как вы закончите обучение, это должно быть очень быстро.


этот код был изменен с как разделить текст без пробелов на список слов?:

from math import log

words = open("english125k.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)

def infer_spaces(s):
    """Uses dynamic programming to infer the location of spaces in a string
    without spaces."""

    # Find the best match for the i first characters, assuming cost has
    # been built for the i-1 first characters.
    # Returns a pair (match_cost, match_length).
    def best_match(i):
        candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
        return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)

    # Build the cost array.
    cost = [0]
    for i in range(1,len(s)+1):
        c,k = best_match(i)
        cost.append(c)

    # Backtrack to recover the minimal-cost string.
    costsum = 0
    i = len(s)
    while i>0:
        c,k = best_match(i)
        assert c == cost[i]
        costsum += c
        i -= k

    return costsum

используя тот же словарь, что ответ и тестирование строковых выходов

>>> infer_spaces("hithisisastringthatmustbechecked")
294.99768817854056

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