【问题标题】:Mathematica's pattern matching poorly optimized?Mathematica 的模式匹配优化不佳?
【发布时间】:2011-12-15 15:56:36
【问题描述】:

我最近询问了为什么PatternTest 会引起大量不必要的评估:PatternTest not optimized? Leonid 回答说,在我看来这是一种相当有问题的方法是必要的。我可以接受,但我更喜欢更有效的选择。

我现在意识到,我相信 Leonid 已经说过一段时间了,这个问题在 Mathematica 中运行得更深,我很困扰。我不明白为什么这不是或不能更好地优化。

考虑这个例子:

list = RandomReal[9, 20000];
Head /@ list; // Timing
MatchQ[list, {x__Integer, y__}] // Timing
{0., 空}
{1.014, 错误}

检查列表的头部基本上是瞬时的,但检查模式需要一秒钟。当然,Mathematica 可以认识到,由于列表的第一个元素不是整数,因此模式无法匹配,并且与 PatternTest 的情况不同,我看不出模式中存在任何可变性。对此有何解释?


关于打包数组似乎有些混乱,据我所知,这与这个问题无关。相反,我关心的是所有列表(打包或解包)的 O(n2) 时间复杂度。

【问题讨论】:

    标签: performance wolfram-mathematica pattern-matching


    【解决方案1】:

    MatchQ 为这些类型的测试解包。原因是没有实施任何特殊情况。原则上它可以包含任何东西。

    On["Packing"]
    MatchQ[list, {x_Integer, y__}] // Timing
    
    MatchQ[list, {x__Integer, y__}] // Timing
    

    改善这一点非常棘手 - 如果你破坏了模式匹配器,你就会遇到严重的问题。

    编辑 1: 确实,拆包不是 O(n^2) 复杂性的原因。但是,它确实表明,对于MatchQ[list, {x__Integer, y__}] 部分,代码转到了算法的另一部分(需要解压缩列表)。需要注意的其他一些事项:仅当两个模式都是 __ 时才会出现这种复杂性,如果其中一个模式是 _,则算法具有更好的复杂性。

    然后该算法会遍历所有 n*n 个潜在匹配项,并且似乎没有提前救助。大概是因为可以构建需要这种复杂性的其他模式 - 问题是上述模式迫使匹配器采用非常通用的算法。

    然后我希望MatchQ[list, {Shortest[x__Integer], __}]和朋友们,但无济于事。

    所以,我的两分钱:要么使用不同的模式(并使用 On["Packing"] 来查看它是否进入一般匹配器),要么进行预检查 DeveloperPackedArrayQ[expr] && Head[expr[[1]]]===Integer 或类似的。

    【讨论】:

    • +1。有趣的是,像MatchQ[list, {__Integer}] 这样的情况没有解包,我猜这是实现的特殊情况之一(因为它受到更多限制)。
    • 我想我不明白包装的相关性。您是否看过有关 PatternTest 的问题?在我看来,同样的问题会影响这一点,即使不存在可能改变的测试功能。
    • 我撤回上述声明。在进行匹配之前解包list(使用AppendTo[list, 1];)会产生大致相同的时间,所以真正的问题是匹配而不是解包。
    • 这种类型的东西优化起来相当棘手。 “显而易见”的是,如果按特定顺序进行测试,并且如果有一些聪明的做法可以排除进一步测试的需要,那么在这个例子中就有可能发生短路并使其相当快。要了解实现起来有多么困难,这里有一个例子,它确实需要努力排除所有匹配的可能性。让它比 O(n^2) 更快让我觉得不太可能(Leonid 注意:不,这不是挑战)。 MatchQ[list, {x__/;List[x][[-1]]==list[[19999]], y__}] // 计时
    • @Daniel,我想我的意思是,在没有ConditionPatternTest 的“简单”模式的情况下,不应该有一个额外的逻辑来检查是否存在短路-电路?
    【解决方案2】:

    @the author of the first answer。据我从逆向工程和对可用信息的阅读中了解到,这可能是由于检查模式的方式不同。事实上 - 正如他们所说 - 一个特殊的哈希码用于模式匹配。这种散列(基本上是 FNV-1 轮次)使得检查与所涉及的表达式类型相关的特定模式变得非常容易(一些 xor 操作的问题)。哈希算法在表达式内循环,每个子部分与前一个子部分的输出进行异或。每个原子表达式都使用特殊的异或值——machineInts、machineReals、bigNums、Rationals 等等。因此,例如,_Integer 很容易检查,因为任何整数的哈希都是由整数的 xor 值形成的,所以我们需要做的就是进行逆运算并查看是否匹配 - 即,如果我们得到一些特定的值或其他东西像那样(对不起,如果我对实际的实现细节含糊不清。它是 WIP)。对于一般或不常见的模式,检查可能不会利用这种散列内容并需要不同的东西。

    @the OP Head[] 只是作用于内部表达式,获取表达式的第一个指针的值(表达式实现为指针数组)。所以这样做就像复制和打印字符串一样简单 - 非常非常快。在这种情况下甚至没有调用模式匹配引擎。

    【讨论】:

      猜你喜欢
      • 2019-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-28
      • 2023-03-24
      • 2013-09-25
      相关资源
      最近更新 更多