如何确定一个正则表达式是否与另一个正则表达式正交?

16

我想用一个(简化的)例子来解释我的问题。

正则表达式 1:

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

正则表达式 2:

^\d*$

正则表达式1不会匹配正则表达式2匹配的字符串。因此,我们可以说正则表达式1是与正则表达式2正交的。

由于许多人询问我所说的“正交”的含义,我将尝试澄清:

S1成为正则表达式1匹配的(无限)字符串集合。S2是正则表达式2匹配的字符串集合。如果S1和S2的交集为空,则正则表达式2与正则表达式1正交。例如,正则表达式'^\d_a$'不是正交的,因为字符串'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);
13个回答

10

“Orthogonal”是指“交集为空集”,对吗?

我会构建正则表达式来表示交集,然后将其转换为正常形式的正则文法,并查看它是否是空语言...

不过,我是一个理论家...


有没有算法可以计算两个给定正则表达式的交集的正则表达式,然后将其转换为正常形式,或者这些操作都需要手动完成? - Jeremy Bourque
有算法。我在工作中没有我的理论文本,但在最坏的情况下,它涉及转换为DFAs,然后创建一个交集DFA来跟踪其他DFAs中每个状态所处的位置。然后你将其转换回正常形式的常规语法... - Brian Postow
任何计算理论的文本都应该包含它。我手头没有任何在线参考资料... - Brian Postow
我不是一个理论家,我的第一个想法是将RE转换为DFA或NFA(可能是DFA),然后从那里开始... - Thomas Owens

7

这个问题已经发布两年了,但是我很高兴地告诉大家,现在可以通过调用“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"

希望这可以帮到你!

非常好!谢谢。我测试了几个例子,但是我注意到有些并不像我预期的那样工作。例如:运行ghc Main.hs '710/([a-z]){4,8}/..jpg' '710.' 什么也没有返回。 - Benedikt Waldvogel
没错,这是因为“.*”默认被翻译成了{0,3}。试试这个:genex '710/([a-z]){4,8}/.*.jpg' '710.{4,10}'希望能有所帮助! - audreyt
1
@audreyt 这似乎是作弊。这是否意味着您的方法无法证明这些正则表达式不相交:^1(11)*$^(11)*$ - Kendall Hopkins

7
我会构建交集的正则表达式,然后将其转换为正常形式的正则语法,并查看它是否为空语言。这似乎有点大材小用了。为什么不只是构建乘积自动机并检查是否可以从初始状态到达接受状态呢?这还会直接给您一个在交集中的字符串,而无需首先构造正则表达式。
我只知道一种方法,即从regexp创建DFA,它是指数时间(在退化情况下)。它可归约为停机问题,因为所有内容都是,但停机问题不能归约为它。
如果是这样的话,那么您可以利用任何RE都可以转换为有限状态机的事实。如果两个有限状态机相等,则它们具有相同的节点集,以及连接这些节点的相同弧。因此,根据我认为您正在使用的正交定义,如果将您的RE翻译成FSM并且这些FSM不相等,则RE正交。
那不是正确的。您可以有两个DFA(FSM),它们在边缘标记的多图意义上不同构,但接受相同的语言。此外,如果没有这种情况,您的测试将检查两个正则表达式是否接受非相同的内容,而OP想要非重叠的语言(空交集)。
此外,请注意,\1、\2...、\9构造不是正则的:它不能用串联、并集和*(Kleene星号)表示。如果要包括回溯替换,我不知道答案是什么。同样值得注意的是,上下文无关语言的相应问题是不可判定的:不存在算法可以接受两个上下文无关文法G1和G2,并返回true iff L(G1) ∩ L(g2) ≠ Ø。

@Brian Postow。毫无疑问,一切都可以归结为停机问题,包括程序等价性(我可以保证,如果你给我一个解决停机问题的程序H,我将能够给你一个解决程序等价性问题的程序)。 - psmears
当你说“那不是减少的工作方式”时,你必须定义你所说的减少的类型 :) 可能有许多可能的意义; 其中一个是“给定一个满足某些属性的程序P,它解决问题A,将其转换为解决问题B的程序P'”; 另一个涉及转换问题实例; 另一个涉及创建具有访问oracle权限的程序。在不同的定义下,“X是否减少到Y”对于相同的X和Y将有不同的答案。我的观点是,原始语句存在一种有意义的意义 :) - psmears
@psmears:也许是这样,但是研究不可解问题的约简的人们都不认为“不存在Halt程序,因此任何以'如果你有一个Halt程序,那么你就可以...'开头的语句都是真实的”是一个合理的约简陈述... - Brian Postow
@Brian Postow:当然,如果他们有任何数学背景,他们会这样做!这个语句听起来奇怪的原因是使用这个规约定义的有用方式是相反的(即,如果我有一个问题P,而你可以展示停机问题规约 P,那么我们立刻知道P是不可判定的)。我认为最初的观点是这一点 - 即其他人错误地谈论了规约(“这个问题规约到停机问题”并没有告诉我们实际想知道的内容 :)。 - psmears
@psmears。这取决于我们想要知道什么...使用“约简”的正常定义,仍然不是“一切都可以归结为停机问题”。通常情况下,我们看待Halt会归结为其他问题,你是对的,但在复杂性理论的其他部分,我们看待事物会归结为Halt...并且程序等价性不会归结为Halt。 - Brian Postow
显示剩余3条评论

2
fsmtools 可以对有限状态机进行各种操作,您唯一的问题是将正则表达式的字符串表示转换为 fsmtools 可以处理的格式。这对于简单情况来说肯定是可行的,但是在存在高级功能(如前瞻、后顾)的情况下会比较棘手。
您还可以看一下 OpenFst,尽管我从未使用过它。它支持交集操作。

2

关于 \1、\2 的优秀观点…这是无上下文的,所以不可解决。小问题:并非所有东西都可以归约到 Halt…例如程序等价性。- Brian Postow

[我在回复评论]

IIRC, a^n b^m a^n b^m 不是无上下文的,因此 (a\*)(b\*)\1\2 也不是,因为它们是相同的。如果 L 是“好”的语言之一,对于“好”是指 regularcontext-free 中的一个,则 ISTR { ww | w ∈ L } 也不是“好”的。

我修改了我的说法:RE 中的一切都可以归约到停机问题;-)


您的所有观点都是正确的。\ 1等使语言具有上下文敏感性。我不认为它使之图灵完备,但我需要再仔细考虑一下。 - Brian Postow
使用后向引用的模式匹配是NP完全问题——就像http://en.wikipedia.org/wiki/Regular_expression#cite_ref-Aho90_24-0所说,引用{Aho, Alfred V. (1990). "Algorithms for finding patterns in strings". In van Leeuwen, Jan. Handbook of Theoretical Computer Science, volume A: Algorithms and Complexity. The MIT Press. pp. 255–300}的Theorem 6.2。 - Jonas Kölker

2

我终于找到了我一直在寻找的库:

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();
}

需要注意的是,此实现不支持复杂的正则表达式功能,如反向引用。请参阅博客文章"A Faster Java Regex Package",介绍了dk.brics.automaton


1

将每个正则表达式转换为DFA。从一个DFA的接受状态创建一个epsilon转换到第二个DFA的起始状态。通过添加epsilon转换,您实际上已经创建了一个NFA。然后将NFA转换为DFA。如果起始状态不是接受状态,并且可以到达接受状态,则两个正则表达式不是“正交”的。(因为它们的交集不为空)。

有已知的程序可将正则表达式转换为DFA,并将NFA转换为DFA。您可以查看像Sipser的《计算理论导论》这样的书籍以获取程序,或者在网络上搜索。毫无疑问,许多本科生和研究生必须在某个“理论”课程中完成此操作。


这假设该语言中的字符串严格是连接的。考虑字母表{a,b}上的语言“ ab +”和“ ab *”。在“ ab +”中的所有字符串都在交集中。基本上,仅由“ ab *”匹配但未被“ ab +”匹配的字符串是字符串“ a”。如果我理解得正确的话,您的构造将产生语言“ ab +(ab ”,不是吗? - Bob9630

1
你可以尝试使用类似 Regexp::Genex 的工具来生成测试字符串以匹配指定的正则表达式,然后将测试字符串用于第二个正则表达式,以确定这两个正则表达式是否正交。

如果我理解正确的话,你所提出的是模糊测试。实际上,那不是我正在寻找的。 - Benedikt Waldvogel
是的,绝对不是证明,只是两个黑盒测试。 - codelogic

1

证明一个正则表达式与另一个正则表达式正交有时可能很简单,例如在相同位置的互斥字符组中。但对于除了最简单的正则表达式之外的任何表达式,这都是一个非平凡的问题。对于包含组和反向引用的复杂表达式,我甚至可以说这可能是不可能的。


1

我相信kdgregory是正确的,你使用Orthogonal来表示Complement

这个理解正确吗?


我认为他的意思不是补集,而是交集为空。 - FryGuy
交集往往是可交换的,因为它适用于两个方向。这位作者正在寻找的关系可能没有一个单一的通用名称,但他明确表示这是一种单向关系。 - Sparr
1
这位作者正在寻找的关系可能没有一个通用的名称。不妨考虑“不交”?此外,“正交”通常是对称的(约等于可交换),因此这似乎是一个合理的猜测。 - Jonas Kölker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接