На данный момент я решил взять словарь и повторить все это. Каждый раз, когда я вижу новую строку, я создаю строку, содержащую от этой новой строки до следующей новой строки, затем я выполняю string.find (), чтобы увидеть, находится ли это английское слово где-то там. Это занимает ОЧЕНЬ много времени, каждое слово занимает около 1 / 2-1 / 4 секунды для проверки.
Это работает отлично, но мне нужно проверять тысячи слов в секунду. Я могу запустить несколько окон, которые не влияют на скорость (многопоточность), но все равно проверяет только как 10 в секунду. (Мне нужны тысячи)
В настоящее время я пишу код для предварительной компиляции большого массива, содержащего каждое слово в английском языке, что должно значительно ускорить его, но при этом не получить желаемой скорости. Там имеет чтобы быть лучшим способом сделать это.
Строки, которые я проверяю, будут выглядеть так:
"hithisisastringthatmustbechecked"
но большинство из них содержали полный мусор, просто случайные буквы.
Я не могу проверить невозможность составления букв, потому что эта строка будет выброшена из-за ‘tm’, между ‘thatmust’.
Есть много стратегий сделать это быстро.
Идея 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"
Если письмо присутствует несколько раз, вам нужно сохранить больше букв. Так что этот должен сохранить всю строку, потому что «е» продолжает показывать менее 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 -> *
Стрелки обозначают букву, которая должна следовать за другой буквой. Таким образом, «r» должно быть после «а», иначе оно не может совпадать.
Линии, идущие вниз, обозначают опцию. У вас есть возможные буквы «a или b или j», а затем возможные буквы «i или o» после буквы «b».
Регулярное выражение выглядит примерно так: / (arun) | (b (ill (y +)) | (o (b | dy)))) | (jose) / (хотя я мог подсунуть парень). Это дает суть его создания в качестве регулярного выражения.
Как только вы построите эту структуру, вы примените ее к вашей строке, начиная с первого столбца. Попробуйте запустить совпадение, проверив альтернативы, и, если один из них найдется, продвиньтесь вперед и попробуйте букву после стрелки и ее альтернатив. Если вы дойдете до звезды / звездочки, она совпадает. Если у вас закончились альтернативы, в том числе возврат, вы переходите к следующему столбцу.
Это много работы, но иногда может быть удобно.
Примечание Я построил один из них некоторое время назад, написав программу, которая написала код, который запускал алгоритм напрямую, вместо того, чтобы код просматривал структуру данных двоичного дерева.
Представьте, что каждый набор параметров вертикальной черты является switch
оператор против определенного символа столбца и каждая стрелка превращается во вложенность. Если есть только один вариант, вам не нужен полный switch
заявление, просто if
,
Это было какое-то быстрое сопоставление персонажей, и оно очень удобно по какой-то причине, которая ускользает от меня сегодня.
Вы можете ускорить поиск, используя Алгоритм Кнута-Морриса-Пратта (KMP).
Просмотрите каждое словарное слово и создать для него таблицу поиска. Вам нужно сделать это только один раз. Теперь ваш поиск отдельных слов будет проходить в более быстром темпе, потому что «фальстарты» будут устранены.
Как насчет Bloom Filter?
Фильтр Блума, созданный Бертоном Ховардом Блумом в 1970 году, является
пространственно-эффективная структура вероятностных данных, используемая для проверки
является ли элемент членом набора. Ложные положительные совпадения
возможно, но ложных негативов нет; то есть запрос возвращает либо
«внутри набора (может быть неправильно)» или «определенно не в наборе». Элементы могут
быть добавленным в набор, но не удаленным (хотя это можно решить
с «счетным» фильтром). Чем больше элементов, которые добавляются к
установить, тем больше вероятность ложных срабатываний.
Подход может работать следующим образом: вы создаете набор слов, с которыми хотите проверять (это делается только один раз), а затем вы можете быстро выполнить проверку «in / not-in» для каждой подстроки. Если результат «не в», вы можете продолжить (фильтры Блума не дают ложных негативов). Если результат «в», то вы выполняете более сложную проверку для подтверждения (фильтры Блума могут давать ложные срабатывания).
Насколько я понимаю, некоторые средства проверки орфографии полагаются на фильтры Блума, чтобы быстро проверить, относится ли ваше последнее слово к словарю известных слов.
Теоретически, я думаю, вы должны быть в состоянии обучить марковскую модель и использовать ее, чтобы решить, является ли строка предложением или мусором. Есть еще один вопрос о том, как распознать слова, а не предложения: Как определить, звучит ли случайная строка как английская?
Единственная разница для обучения предложениям состоит в том, что ваши таблицы вероятностей будут немного больше. Однако, по моему опыту, современный настольный компьютер имеет более чем достаточно ОЗУ для обработки матриц Маркова, если вы не обучаетесь по всей Библиотеке Конгресса (что не нужно — даже 5 или более книг разных авторов должно быть достаточно для очень точной классификации) ,
Поскольку ваши предложения объединяются без четких границ слов, это немного сложно, но хорошая новость заключается в том, что марковская модель не заботится о словах, только о том, что следует за чем. Таким образом, вы можете заставить его игнорировать пробелы, сначала удалив все пробелы из ваших тренировочных данных. Если вы собираетесь использовать Алису в стране чудес в качестве учебного текста, первый абзац, возможно, будет выглядеть так:
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
, так как это разделит все на однобуквенные слова).