【问题标题】:Algorithmic complexity of Regular Languages in Extended Regular Language frameworks扩展正则语言框架中正则语言的算法复杂度
【发布时间】:2013-12-29 23:29:43
【问题描述】:

我在形式语言方面有一点背景,最近我发现 Java 和其他语言使用所谓的扩展正则语言。由于我的背景,当我为Pattern 调用compile 时,我总是假设在Java 等语言中它会在后台生成一个DFA 或Transducer。结果,我总是假设无论我的正则表达式多么丑陋,无论我的正则表达式、Pattern.matches 或类似方法会在线性时间内运行多长时间。但这个假设似乎是incorrect

我读到的post 似乎暗示一些正则表达式确实在线性时间内运行,但我并不完全相信或信任一个人。

我最终会编写自己的 Java 形式正则表达式库(我发现现有的只有 GNU GPL 许可证),但与此同时,我对 Java/C# 正则表达式的时间复杂度有一些疑问。想确保我读到的elsewhere 是正确的。

问题:

  1. 像 \sRT\s. 这样的形式语言,Java/C# 中正则表达式的匹配方法会在线性时间还是非线性时间解决隶属关系?
  2. 一般来说,我如何知道给定正则语言的表达式成员问题是否是正则表达式的线性时间?

我做文本分析,发现 Java 正则表达式不是 DFA 的,真是令人沮丧。

【问题讨论】:

  • 这可能是cs.stackexchange.com的问题
  • 我很久以前就了解了 DFA,我记得他们可以告诉你模式是否匹配,但他们无法返回捕获组所需的额外信息,这些信息非常 重要。捕获组是正则表达式具有“贪婪”和“不情愿”限定符的原因(即.*.*?);如果匹配的唯一目的是返回真/假,那么这些将无关紧要,并且它们与 DFA 无关。然而,那是几十年前的事了,我不知道从那以后 DFA 理论发展了多少。
  • @ajb 您可能一直在处理简化的 DFA。例如,Mealy Automata 可用于输出等效的捕获组。我相信扩展正则表达式和正式正则语言的唯一区别就是反向引用。
  • @hatchet 我你建议我把这个转发到那里?
  • 您可能对this series of articles感兴趣。

标签: c# java regex time-complexity


【解决方案1】:

由于我的背景,当我为 Pattern 调用 compile 时,我一直假设在 Java 等语言中它会在后台生成 DFA 或 Transducer。

这种信念在学术界很普遍。在实践中,正则表达式编译不会产生 DFA 然后执行它。我在这方面只有少量经验;我在 1990 年代 Microsoft 的 JavaScript 实现中简要研究了正则表达式编译系统。我们选择将“正则”表达式编译成简单的特定领域字节码语言,然后为该语言构建一个解释器。

正如您所注意到的,这可能会导致重复回溯在输入长度上具有指数级不良时间行为的情况,但编译状态的构造在表达式的大小上基本上是线性的。

所以让我用另外两个问题来反驳你的问题 - 我注意到这些是真正的问题,而不是修辞。

1) 每个实际上 正则表达式都对应于一个具有 n 个状态的 NDFA。相应的 DFA 可能需要最多 2n 个状态的叠加。那么是什么阻止了构建 DFA 所花费的时间在病理情况下呈指数增长呢?运行时间在输入中可能是线性的,但如果运行时间在模式大小上是指数的,那么基本上你只是在用一种非线性换另一种。

2) 如今,所谓的“常规”表达式已经不是那种东西了;他们可以进行括号匹配。它们对应于下推自动机,而不是非确定性有限自动机。是否有一种线性算法可以为“正则”表达式构造相应的下推自动机?

【讨论】:

  • 您好,感谢您的提问。对于 1)“通常”,您的正则表达式小于您匹配的字符串。因此,仅通过 big-O,构建用于一场比赛的 DFA 可能比 Java 的 Regex 更快。即使 NFA 中的状态计数大于输入字符串,当您开始每天向 DFA 抛出数百万个字符串时,O(2^n) 的构建时间也与上帝的常数相形见绌。但是假设我们坚持使用具有 n 个状态的 NFA,成员资格问题只有 O(nm),其中 m 是输入字符串的大小。
  • 2) 这来自最近的一些结果。 CFG 下推的两个方向都可以在线性时间和空间中完成。扩展正则表达式不是标准化的。因此,对于 CFG 的正则表达式,您可以在线性时间内构造一个 PDA(假设 Regex -> CFG 仅将输入大小扩展为一个常数)。但事情是这样的,并不是所有的编程语言都有 CFG 的正则表达式!!!!所以对他们来说,你不能把它们翻译成PDA的时期。 Ruby 是个小恶魔:stackoverflow.com/a/8964806/1541137
【解决方案2】:

正则表达式在许多语言中作为 NFA 实现以支持回溯(请参阅http://msdn.microsoft.com/en-us/library/e347654k(v=vs.110).aspx)。由于回溯,您可以构造在某些字符串上性能很差的正则表达式(参见http://www.regular-expressions.info/catastrophic.html

至于分析正则表达式以确定它们的性能,我怀疑一般来说有一个很好的方法。您可以查找警告标志,例如第二个链接示例中的复合回溯,但即使在某些情况下也很难正确检测到。

【讨论】:

  • “实现了正则表达式 [...] 以支持回溯”。回溯并不是一个真正的特性,它是一个实现细节。你指的是反向引用吗?
  • @Kobi 我认为他的意思是反向引用意味着他们需要回溯。令我惊讶的是,当正则表达式 Java、C# 和许多其他语言中没有反向引用时,仍然使用回溯方法(在没有反向引用的真正 NFA 中,您不需要回溯)。
【解决方案3】:

Java 的正则表达式实现使用 NFA 方法。 这是一个link,解释得很清楚。

基本上,写得不好但仍然正确的正则表达式会导致引擎性能不佳。 例如,给定表达式(a+a+)+b 和字符串aaaaaaaaaaaaaaa。 可能需要一段时间(取决于您的机器,从几秒到几分钟不等)才能确定不匹配。

NFA 的最差表现是几乎匹配的情况(给定示例)。因为表达式强制引擎探索每条路径(有很多回溯)以确定不匹配。

【讨论】:

  • 这篇文章使用了错误的术语,它解释为“NFA 引擎被赋予了更多的权力,使其在计算能力上优于 DFA 引擎。” NFA 的成员资格仍然是一个线性问题,因此“指数”时间成员资格意味着它不是真正的 NFA。正如我在原始答案中所要求的,它仍然为stackoverflow.com/questions/4378455/… 提供支持。非常感谢。
猜你喜欢
  • 2016-12-02
  • 2011-12-17
  • 2020-07-11
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 2014-12-21
  • 2013-11-18
  • 2015-04-11
相关资源
最近更新 更多