【问题标题】:How to determine if a regex is orthogonal to another regex?如何确定一个正则表达式是否与另一个正则表达式正交?
【发布时间】:2010-10-04 02:19:52
【问题描述】:

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

正则表达式 1:

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

正则表达式 2:

^\d*$

Regex 1 将从不匹配 regex 2 匹配的字符串。 因此,假设正则表达式 1 与正则表达式 2正交

正如许多人问我所说的正交是什么意思,我会试着澄清一下:

S1 是正则表达式 1 匹配的(无限)字符串集。 S2 是正则表达式 2 匹配的字符串集。 正则表达式 2 与正则表达式 1 正交 iff S1 和 S2 的交集为空。 正则表达式 ^\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);

【问题讨论】:

    标签: regex fsm


    【解决方案1】:

    您也许可以使用Regexp::Genex 之类的东西来生成测试字符串以匹配指定的正则表达式,然后在第二个正则表达式上使用测试字符串来确定这两个正则表达式是否正交。

    【讨论】:

    • 如果我猜对了,那你的提议就会变得模糊不清。其实这不是我要找的。​​span>
    • 是的,绝对不是证明,只是两个黑盒测试。
    【解决方案2】:

    在某些情况下证明一个正则表达式与另一个正则表达式正交可能是微不足道的,例如在相同位置的互斥字符组。对于除了最简单的正则表达式之外的任何其他表达式,这都是一个不平凡的问题。对于带有组和反向引用的严肃表达,我什至会说这可能是不可能的。

    【讨论】:

      【解决方案3】:

      “正交”是指“交集是空集”我接受吗?

      我会构造交集的正则表达式,然后转换成正规形式的正则文法,看看是不是空语言……

      话说回来,我是个理论家……

      【讨论】:

      • 是否有算法来计算两个给定正则表达式的交集的正则表达式,然后将其转换为范式,或者那些是手动操作?
      • 有算法。我没有任何我的理论文本在这里工作,但在最坏的情况下,它涉及转换为 DFA,然后创建一个交叉 DFA 来跟踪每个其他 DFA 将处于哪个状态。然后你转换回来以正常形式的常规语法...
      • 任何计算理论文本都应该有它。我手头没有任何在线参考资料...
      • 我不是理论家,我的第一个想法是将 RE 转换为 DFA 或 NFA(可能是 DFA)并从那里开始......
      【解决方案4】:

      我相信kdgregory 是正确的,您使用正交表示Complement

      这是正确的吗?

      【讨论】:

      • 我不认为他的意思是补充,但交叉点是空的。
      • 交集往往是可交换的(?),因为它适用于两种方式。这位作者正在寻找的关系可能没有一个通用名称,但他明确指出这是一种单向关系。
      • "作者寻找的关系可能没有一个共同的名字" -- `disjoint' 怎么样?此外,正交通常是对称的(~= 可交换的),所以这似乎是一个合理的猜测。
      【解决方案5】:

      首先让我说我不知道​​如何构建这样的算法,我也不知道有任何库实现它。但是,如果得知任意复杂度的一般正则表达式不存在这样的情况,我一点也不感到惊讶。

      每个正则表达式都定义了可以由表达式生成的所有字符串的正则语言,或者如果您愿意,也可以定义正则表达式“匹配”的所有字符串的正则语言。将语言视为一组字符串。在大多数情况下,该集合将无限大。您的问题是询问正则表达式给出的两个集合的交集是否为空。

      至少在第一个近似值上,我无法想象在不计算集合的情况下回答这个问题的方法,对于无限集合来说,这需要比你更长的时间。我认为可能有一种方法可以计算有限的集合并确定何时对模式进行了超出其他正则表达式的要求,但这并不简单。

      例如,只需考虑简单的表达式(ab)*(aba)*b。什么算法会决定从第一个表达式生成 abab 然后停止,而不检查 ababababababab 等,因为它们永远不会工作?您不能只生成字符串并检查直到找到匹配项,因为当语言不相交时,这永远不会完成。我无法想象在一般情况下会有什么效果,但是在这种事情上有些人比我好得多。

      总而言之,这是一个难题。得知有一个多项式时间解决方案我会有点惊讶,而得知它等同于停止问题我一点也不惊讶。虽然,鉴于正则表达式不是图灵完备的,但似乎至少有可能存在解决方案。

      【讨论】:

      • 这就是我在这里发布这个问题的原因;)
      【解决方案6】:

      我会做以下事情:

      • 将每个正则表达式转换为 FSA,使用类似以下结构:

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

      请注意,这不是微不足道的,但对于简单的正则表达式应该没有那么困难。

      • 创建一个新的组合节点,如:

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

      根据左右两边的相同字符建立链接,你会得到两个FSANodes,它们构成一个新的CombinedNode。

      然后从CombinedNode(leftStart, rightStart)开始,找到spanning set,如果有无效的CombinedNodes,则该set不是“正交的”。

      【讨论】:

        【解决方案7】:

        fsmtools 可以在有限状态机上进行各种操作,您唯一的问题是将正则表达式的字符串表示形式转换为 fsmtools 可以使用的格式。对于简单的情况,这绝对是可能的,但在存在诸如look{ahead,behind}之类的高级功能时会很棘手。

        你也可以看看OpenFst,虽然我从来没有用过。不过它支持交集。

        【讨论】:

          【解决方案8】:

          我会构造交集的正则表达式,然后转换成正规形式的正则文法,看看是不是空语言……

          这好像是用大炮射麻雀。为什么不直接构建产品自动机并检查是否可以从初始状态到达接受状态?这也将立即在交集处为您提供一个字符串,而无需先构造正则表达式。

          得知存在多项式时间解我会有点惊讶,而得知它等同于停止问题我一点也不惊讶。

          我只知道一种方法,它涉及从正则表达式创建 DFA,这是指数时间(在退化的情况下)。它可以还原为停机问题,因为一切都是,但是停机问题不能还原为

          如果是最后一个,那么您可以使用任何 RE 都可以转换为有限状态机的事实。如果两个有限状态机具有相同的节点集,则它们相等,并且连接这些节点的弧线相同。

          因此,鉴于我认为您使用的正交定义,如果您将 RE 转换为 FSM 并且这些 FSM 不相等,则 RE 是正交的。

          这是不正确的。您可以有两个在边缘标记的多图意义上是非同构的 DFA (FSM),但接受相同的语言。此外,如果不是这种情况,您的测试将检查两个正则表达式是否接受非相同,而 OP 需要非重叠语言(空交集)。


          另外,请注意,\1、\2、...、\9 结构是不规则的:它不能用连接、联合和 *(Kleene 星号)表示。如果你想包括反向替换,我不知道答案是什么。同样有趣的是,上下文无关语言的相应问题是不可判定的:没有算法可以采用两个上下文无关文法 G1 和 G2 并在当且仅当 L(G1) ∩ L(g2) ≠ Ø 时返回真。

          【讨论】:

          • \1、\2 位上的优点...这是上下文无关的,因此无法解决。次要观点:并非所有事情都可以归结为停止...例如程序等价..
          • @Brian Postow。当然,一切都可以归结为停机问题,包括程序等价性(在某种意义上,我可以保证,如果你给我一个解决停机问题的程序 H,我将能够给你一个解决程序等价问题的程序)?
          • @psmears。没有。即使你给了我一个解决停止问题的程序,我仍然无法解决程序等价问题。有一整套事情比停止更难......
          • @psmears 除非,你说的是 False -> False 是真的……但这不是减少的工作原理……
          • @Brian Postow:当你说“这不是减少的工作方式”时,你必须定义你的意思是什么类型的减少 :) 有多种可能的含义;一个是“给定一个解决问题 A 的程序 P(满足某些属性),将其转换为解决问题 B 的程序 P'”;另一个涉及转换问题实例;另一个涉及创建一个可以访问预言机的程序。在不同的定义下,“X 是否简化为 Y”对于相同的 X 和 Y 会有不同的答案。我的观点是,确实存在一种有意义的意义,即原始陈述为真 :)
          【解决方案9】:

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

          存在将正则表达式转换为 DFA 以及将 NFA 转换为 DFA 的已知过程。您可以查看 Sipser 的“计算理论导论”之类的书来了解这些过程,或者只是在网上搜索。毫无疑问,许多本科生和研究生必须为一个或另一个“理论”课这样做。

          【讨论】:

          • 这假定语言中的字符串是严格的连接。考虑字母表 {a,b} 上的语言“ab+”和“ab*”。 "ab+" 中的所有字符串都在交集中。几乎唯一与“ab*”匹配但与“ab+”不匹配的字符串是字符串“a”。假设我理解正确,您的构造会产生语言“ab+(ab*)*”,不是吗?
          【解决方案10】:

          我说得太早了。我在原始帖子中所说的内容不会奏效,但是如果您可以将正则表达式转换为 DFA 形式,那么您将尝试执行一个过程。

          您可以在我在第一篇文章中提到的书中找到该过程:Sipser 的“计算理论导论”第 2 版。它在第 46 页,脚注中有详细信息。

          该过程将为您提供一个新的 DFA,它是两个 DFA 的交集。如果新的 DFA 具有可到达的接受状态,则交集是非空的。

          【讨论】:

            【解决方案11】:

            \1、\2 位的优点...这是上下文无关的,因此无法解决。次要观点:并非一切都可以归结为停止...例如程序等效性.. – Brian Postow

            [我正在回复评论]

            IIRC,a^n b^m a^n b^m 不是上下文无关的,因此 (a\*)(b\*)\1\2 也不是,因为它是相同的。 ISTR { ww | w ∈ L } 不是“好”,即使 L 是“好”,因为很高兴成为 regularcontext-free 之一。

            我修改我的声明:RE 中的所有内容都可以归结为停机问题 ;-)

            【讨论】:

            • 您在所有方面都是正确的。 \1 等使语言上下文敏感。我不认为它会使其图灵完整,但我必须更加努力地考虑这一点。
            • 与反向引用的模式匹配是 NP 完全的---so sayeth en.wikipedia.org/wiki/Regular_expression#cite_ref-Aho90_24-0,引用了 {Aho, Alfred V. (1990) 的定理 6.2。 “在字符串中查找模式的算法”。在 van Leeuwen,Jan. 理论计算机科学手册,卷 A:算法和复杂性。麻省理工学院出版社。第 255–300 页}。
            【解决方案12】:

            这个问题发布已经两年了,但我很高兴地说,现在只需在此处调用“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"
            

            希望这会有所帮助!

            【讨论】:

            • 非常好!谢谢。我已经测试了一些示例,但我注意到有些工作不如我预期。示例:runghc Main.hs '710/([a-z]){4,8}/.*.jpg' '710.*' 什么都不返回。
            • 对,那是因为“.*”默认翻译为{0,3}。试试这个:genex '710/([a-z]){4,8}/.*.jpg' '710.{4,10}' 希望有帮助!
            • @audreyt 这似乎是作弊。这是否意味着您的方法无法证明这些正则表达式不相交:^1(11)*$^(11)*$
            【解决方案13】:

            我终于找到了我要找的图书馆:

            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 功能,例如反向引用。请参阅博客文章"A Faster Java Regex Package",其中介绍了dk.brics.automaton

            【讨论】:

              猜你喜欢
              • 2013-09-14
              • 2012-04-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-11-27
              • 1970-01-01
              • 2013-07-13
              • 1970-01-01
              相关资源
              最近更新 更多