Как определить, является ли регулярное выражение ортогональным к другому регулярному выражению?

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

Regex 1:

^\d+_[a-z]+$

Regex 2:

^\d*$

Regex 1 будет никогда соответствовать строке, где соответствует регулярное выражение 2. Итак, пусть говорят, что регулярное выражение 1 ортогонально регулярному выражению 2.

Как многие люди спрашивали, что я подразумеваю под ортогональным, я попытаюсь прояснить это:

Пусть S1 - (бесконечное) множество строк, где соответствует регулярное выражение 1. S2 - это набор строк, в которых соответствует регулярное выражение 2. Regex 2 ортогонален регулярному выражению 1, если пересечение S1 и S2 пусто. Регулярное выражение ^\d_a $будет не ортогональным, поскольку String '2_a' находится в наборе S1 и S2.

Как это можно определить программным путем, если два регулярных выражения ортогональны друг другу?

Лучшим случаем будет некоторая библиотека, которая реализует такой метод, как:

/**
 * @return True if the regex is orthogonal (i.e. "intersection is empty"), False otherwise or Null if it can't be determined
 */
public Boolean isRegexOrthogonal(Pattern regex1, Pattern regex2);

Ответы

Ответ 1

Наконец-то я нашел именно ту библиотеку, которую я искал:

dk.brics.automaton

Использование:

/**
 * @return true if the two regexes will never both match a given string
 */
public boolean isRegexOrthogonal( String regex1, String regex2 ) {
   Automaton automaton1 = new RegExp(regex1).toAutomaton();
   Automaton automaton2 = new RegExp(regex2).toAutomaton();
   return automaton1.intersection(automaton2).isEmpty();
}

Следует отметить, что реализация не поддерживает и не может поддерживать сложные функции RegEx, такие как обратные ссылки. См. Сообщение в блоге "Более быстрый пакет Java Regex" , в котором представлены dk.brics.automaton.

Ответ 2

Под "ортогональным" вы подразумеваете "пересечение - пустое множество", я беру его?

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

И снова я теоретик...

Ответ 3

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

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

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

Я знаю только способ сделать это, что связано с созданием DFA из регулярного выражения, которое является экспоненциальным временем (в вырожденном случае). Это сводится к проблеме остановки, потому что все есть, но проблема с остановкой не сводится к ней.

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

Итак, учитывая, что я использую в качестве определения для ортогональных, если вы переводите свои RE в FSM и эти FSM не равны, REs являются ортогональными.

Это не правильно. Вы можете иметь два DFAs (FSM), которые неизоморфны в методе, отмеченном краями, и принимают одинаковые языки. Кроме того, если бы это было не так, ваш тест проверял, принимали ли два регулярных выражения не идентичные, тогда как OP хочет неперекрывающиеся языки (пустое пересечение).


Кроме того, имейте в виду, что конструкция \1,\2,...,\9 не является регулярной: она не может быть выражена в терминах конкатенации, объединения и * (звезда Клейна). Если вы хотите включить обратную замену, я не знаю, что такое ответ. Также интересен тот факт, что соответствующая проблема для контекстно-свободных языков неразрешима: нет алгоритма, который принимает два контекстно-свободных грамматики G1 и G2 и возвращает true, если L (G1) ∩ L (g2) ≠ Ø.

Ответ 4

Прошло два года с момента публикации этого вопроса, но я рад сказать, что теперь это можно определить, просто называя здесь программу "genex": https://github.com/audreyt/regex-genex

$ ./binaries/osx/genex '^\d+_[a-z]+$' '^\d*$'
$

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

$ runghc Main.hs '\d' '[123abc]' 
1.00000000      "2"
1.00000000      "3"
1.00000000      "1"

Надеюсь, это поможет!

Ответ 5

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

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

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

Например, просто рассмотрите простые выражения (ab)* и (aba)*b. Что такое алгоритм, который решит сгенерировать abab из первого выражения, а затем остановится, не проверив ababab, abababab и т.д., Потому что они никогда не будут работать? Вы не можете просто генерировать строки и проверять до тех пор, пока не будет найдено совпадение, потому что это никогда не завершится, если языки не пересекаются. Я не могу представить ничего, что могло бы работать в общем случае, но тогда люди гораздо лучше, чем я, при этом.

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

Ответ 6

fsmtools может выполнять все виды операций на конечных машинах, единственной проблемой будет преобразование строкового представления регулярное выражение в формате, с которым может работать fsmtools. Это, безусловно, возможно для простых случаев, но будет сложным при наличии расширенных функций, таких как look {ahead, behind}.

Вы также можете посмотреть OpenFst, хотя я никогда не использовал его. Однако он поддерживает пересечение.

Ответ 7

Отличная точка на \1,\2 бит... что контекст свободен, и поэтому не разрешается. Незначительная точка: не все сводится к Halt... Программа эквивалентности, например.. - Brian Postow

[Я отвечаю на комментарий]

IIRC, a^n b^m a^n b^m не является контекстом, и поэтому (a\*)(b\*)\1\2 не является ни тем, ни другим. ISTR { ww | w ∈ L } не является "приятным", даже если L "приятный", поскольку он является одним из regular, context-free.

Я изменяю свое утверждение: все в RE сводится к проблеме остановки; -)

Ответ 8

Вы можете использовать что-то вроде Regexp:: Genex для создания тестовых строк для соответствия указанному регулярному выражению, а затем использовать тестовую строку на 2-го регулярного выражения, чтобы определить, являются ли 2 регулярных выражения ортогональными.

Ответ 9

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

Ответ 10

Я считаю, kdgregory правильно, что вы используете Orthogonal для обозначения Complement.

Правильно ли это?

Ответ 11

Я бы сделал следующее:

  • преобразуйте каждое регулярное выражение в FSA, используя что-то вроде следующей структуры:

    struct FSANode
    {
        bool accept;
        Map<char, FSANode> links;
    }
    List<FSANode> nodes;
    FSANode start;
    

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

  • Создайте новый комбинированный Node как:

    class CombinedNode
    {
        CombinedNode(FSANode left, FSANode right)
        {
            this.left = left;
            this.right = right;
        }
    
        Map<char, CombinedNode> links;
        bool valid { get { return !left.accept || !right.accept; } }
    
        public FSANode left;
        public FSANode right;
    }
    

Создайте ссылки, основанные на том же char на левой и правой сторонах, и вы получите два FSANodes, которые создают новый CombinedNode.

Затем запустите в CombinedNode (leftStart, rightStart) и найдите набор spanning, и если есть какие-то недействительные CombinedNodes, набор не является "ортогональным".

Ответ 12

Преобразование каждого регулярного выражения в DFA. Из состояния accept одного DFA создайте переход epsilon в начальное состояние второго DFA. Фактически вы создали NFA, добавив переход epsilon. Затем конвертируйте NFA в DFA. Если начальное состояние не является условием принятия, и состояние принятия доступно, то два регулярных выражения не являются "ортогональными". (Так как их пересечение не пусто).

Известны процедуры преобразования регулярного выражения в DFA и преобразования NFA в DFA. Вы можете взглянуть на книгу типа "Введение в теорию вычислений" Sipser для процедур или просто искать в Интернете. Несомненно, многие магистранты и выпускники должны были делать это для одного класса "теории" или другого.

Ответ 13

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

Вы считаете, что два REs ортогональны, если они частично перекрываются? Или если это подмножество другого? Или просто, если они не могут использоваться для соответствия одному и тому же тексту?

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

Итак, учитывая, что я использую в качестве определения для ортогональных, если вы переводите свои RE в FSM и эти FSM не равны, REs являются ортогональными.

Ответ 14

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

Вы можете найти процедуру в книге, которую я упомянул в своем первом посте: "Введение в теорию вычислений" 2-е издание от Sipser. Это на стр. 46, с подробностями в сноске.

Процедура даст вам новый DFA, который является пересечением двух DFA. Если новый DFA имеет допустимое состояние accept, то пересечение не пусто.