嵌套递归函数的复杂性分析



我已经为一个小的本土计算机代数系统编写了一个递归算法,其中我将成对约简应用于代数运算的操作数列表(仅相邻操作数,因为代数是非交换的(。我试图了解我的算法的运行时复杂性(但不幸的是,作为一名物理学家,我已经很长时间没有参加任何涉及复杂性分析的本科 CS 课程了(。在不详细介绍具体问题的情况下,我认为我可以将算法形式化为函数f,这是一个"除法"步骤和一个组合结果的函数g。然后,我的算法将采用以下形式表示:

f(1) = 1  # recursion anchor for f
f(n) = g(f(n/2), f(n/2))
g(n, 0) = n, g(0, m) = m            # recursion ...
g(1, 0) = g(0, 1) = 1               # ... anchors for g
           / g(g(n-1, 1), m-1)  if reduction is "non-neutral"
g(n, m) = |  g(n-1, m-1)        if reduction is "neutral"
            n + m              if no reduction is possible

在此表示法中,函数fg接收列表作为参数和返回列表,输入/输出列表的长度是参数和上述等式的右侧。

对于完整的故事,对应于fg的实际代码如下:

def _match_replace_binary(cls, ops: list) -> list:
    """Reduce list of `ops`"""
    n = len(ops)
    if n <= 1:
        return ops
    ops_left = ops[:n//2]
    ops_right = ops[n//2:]
    return _match_replace_binary_combine(
            cls,
            _match_replace_binary(cls, ops_left),
            _match_replace_binary(cls, ops_right))

def _match_replace_binary_combine(cls, a: list, b: list) -> list:
    """combine two fully reduced lists a, b"""
    if len(a) == 0 or len(b) == 0:
        return a + b
    if len(a) == 1 and len(b) == 1:
        return a + b
    r = _get_binary_replacement(a[-1], b[0], cls._binary_rules)
    if r is None:
        return a + b
    if r == cls.neutral_element:
        return _match_replace_binary_combine(cls, a[:-1], b[1:])
    r = [r, ]
    return _match_replace_binary_combine(
            cls,
            _match_replace_binary_combine(cls, a[:-1], r),
            b[1:])

我对最坏情况的次数感兴趣get_binary_replacement调用,具体取决于ops的大小

所以我想我现在已经明白了。要重述问题:查找使用大小为 n 的输入调用_match_replace_binary时要_get_binary_replacement的调用数。

  • 定义函数g(n, m)(如原始问题中(,将_match_replace_binary_combine的两个输入的大小映射到输出的大小
  • 定义一个函数T_g(n, m),该函数将 _match_replace_binary_combine 的两个输入的大小映射到获取结果所需的对g的调用总数。这也是(最坏情况(对_get_binary_replacement的调用数,因为每次调用_match_replace_binary_combine呼叫最多_get_binary_replacement一次

我们现在可以考虑g的最坏情况和最佳情况:

  • 最佳情况(无减少(:g(n,m) = n + mT_g(n, m) = 1

  • 最坏情况(所有非中性还原(:g(n, m) = 1T_g(n, m) = 2*(n+m) - 1(我根据经验确定了这一点(

现在,主定理(WP(适用:

浏览WP上的描述:

  • k=1(递归锚点的大小为 1(
  • 我们在恒定(d = 1(时间内分成大小n/2 a = 2子问题
  • 解决子问题后,合并结果所需的工作量是c = T_g(n/2, n/2)。在最坏情况下为 n-1(大约 n(,在最佳情况下为 1

因此,按照主定理WP页面上的示例,最坏情况复杂度为n * log(n),最佳情况复杂度为n

实证试验似乎证实了这一结果。对我的推理路线有什么异议吗?

最新更新