Как вы можете распараллеливать регулярный поиск одной длинной строки?
Я тестирую результат моделирования, чтобы увидеть, входит ли он в цикл в какой-то момент, поэтому мне нужно знать, будет ли результат повторяться. Например, может быть 400 цифр, за которыми следует цикл 400 000 цифр. Выход состоит только из цифр от 0 до 9. У меня есть следующая функция регулярного выражения, которую я использую для соответствия повторений в одной длинной строке:
def repetitions(s):
r = re.compile(r"(.+?)\1+")
for match in r.finditer(s):
if len(match.group(1)) > 1 and len(match.group(0))/len(match.group(1)) > 4:
yield (match.group(1), len(match.group(0))/len(match.group(1)))
Эта функция работает фантастически, но она занимает слишком много времени. Мой последний тест составлял 4 миллиона цифр, и для поиска потребовалось 4,5 часа. Он не нашел повторений, поэтому мне теперь нужно увеличить пространство поиска. Код относится только к подпоследовательности, которые повторяются более чем в 4 раза, потому что я рассматриваю 5 повторений, чтобы дать набор, который можно проверить вручную: симуляция будет генерировать подпоследовательности, которые будут повторяться сотни раз. Я работаю на четырехъядерном компьютере, и цифры, которые нужно проверить, генерируются в реальном времени. Как увеличить скорость поиска?
Ответы
Ответ 1
Основываясь на информации, предоставленной nhahtdh в одном из других ответов, некоторые вещи выявляются.
Во-первых, проблема, которую вы ставите, называется поиском "тандемных повторов" или "квадратов".
Во-вторых, алгоритм, приведенный в http://csiflabs.cs.ucdavis.edu/~gusfield/lineartime.pdf, обнаруживает, что z tandem повторяет в O (n log n + z) время и является "оптимальным" в том смысле, что может быть так много ответов. Возможно, вы сможете использовать параллельный поиск в тандеме, но сначала я делаю тайминги с простодушным подходом и делясь на 4, чтобы увидеть, находится ли это в диапазоне скоростей, который вы ожидаете.
Кроме того, для использования этого подхода вам понадобится O (n) пространство для хранения этого дерева суффиксов. Поэтому, если у вас есть порядка 400 000 цифр, вам понадобится порядка 400 000 раз для сборки и 400 000 байт и сохраните это дерево суффикса.
Я не совсем понимаю, что подразумевается под поиском в режиме реального времени, я обычно думаю об этом как о жестком ограничении того, сколько времени может потребоваться операция. Если это так, то это не произойдет здесь. Этот алгоритм должен читать во всей строке ввода и процессах, которые перед началом получения результатов. В этом смысле это то, что называется "off-line" алгоритмом,.
http://web.cs.ucdavis.edu/~gusfield/strmat.html содержит C-код, который вы можете скачать. (В tar файле strmat.tar.gz найдите repeat_tandem.c и repeatats_tandem.h).
В свете вышеизложенного, если этот алгоритм недостаточно эффективен или эффективен в пространстве, я бы поискал способы изменения или сужения проблемы. Может быть, вам нужно только фиксированное количество ответов (например, до 5)? Если циклы являются результатом выполнения операторов в программе, учитывая, что языки программирования (кроме ассемблера) не имеют произвольных операторов "goto", возможно, что это может сузить типы циклов, которые могут произойти и каким-то образом использовать этой структуры может предложить способ ускорить процесс.
Ответ 2
Когда один алгоритм слишком медленный, переключите алгоритмы.
Если вы ищете повторяющиеся строки, вы можете использовать схему дерева суффиксов: https://en.wikipedia.org/wiki/Suffix_tree
Здесь вы найдете обычные подстроки для линейного времени.
EDIT: @nhahtdh inb комментарий ниже ссылался на документ, в котором рассказывается, как быстро выбрать все тандемные повторы z. Если кто-то упрекает
мой ответ, @nhahtdh должен логически получить часть кредита.
Я не пробовал, но я бы предположил, что вы могли бы распараллелить конструкцию самого дерева суффиксов.
Ответ 3
Я уверен, что есть место для оптимизации, но протестируйте этот алгоритм на более коротких строках, чтобы увидеть, как он сравнивается с вашим текущим решением:
def partial_repeat(string):
l = len(string)
for i in range(2, l//2+1):
s = string[0:i]
multi = l//i-1
factor = l//(i-1)
ls = len(s)
if s*(multi) == string[:ls*(multi)] and len(string)-len(string[:ls*factor]) <= ls and s*2 in string:
return s
>>> test_string
'abc1231231231231'
>>> results = {x for x in (partial_repeat(test_string[i:]) for i in range(len(test_string))) if x}
>>> sorted(sorted(results, key=test_string.index), key=test_string.count, reverse=True)[0]
'123'
В этой тестовой строке неясно, являются ли неповторяющиеся начальные символы 'abc'
или 'abc1'
, поэтому повторяющаяся строка может быть либо '123'
, либо '231'
. Вышеприведенные сортировки кажутся подстрокой по ее раннему появлению в тестовой строке, снова сортируются (sorted()
- стабильный вид) на самой высокой частоте и принимают верхний результат.
Со стандартными циклами и min()
вместо понятий и sorted()
:
>>> g = {partial_repeat(test_string[i:]) for i in range(len(test_string))}
>>> results = set()
>>> for x in g:
... if x and (not results or test_string.count(x) >= min(map(test_string.count, results))):
... results.add(x)
...
>>> min(results, key=test_string.index)
'123'
Я тестировал эти решения с тестовой строкой 'abc123123a'
, умноженной на (n for n in range(100, 10101, 500)
, чтобы получить некоторые данные синхронизации. Я ввел эти данные в Excel и использовал функцию FORECAST()
для оценки времени обработки строки в 4 миллиона символов в 430 секунд или около семи минут.