【问题标题】:Runtime complexity of recursive memoized solution for boolean parenthesization布尔括号的递归记忆解决方案的运行时复杂度
【发布时间】:2016-05-08 05:20:45
【问题描述】:

This是一个经典的算法问题。

DP 解确实是 n^3。

我在下面使用带有记忆的递归。

我需要详细解释一下下面代码的运行时间是什么?我对目前的答案不满意。有人可以帮忙吗?

 public static int countParenthesization(String expr, int begin, int end, boolean result, Map<String, Integer> lookup) {
    String lookupKey = begin + "-" + end + "-" + result;

    if (end - begin == 0) {
        String currenExpr = expr.charAt(begin) + "";
        int count = (currenExpr.equals("T") && result) || (currenExpr.equals("F") && !result) ? 1 : 0;
        lookup.put(lookupKey, count);
        return count;
    }

    if (lookup.containsKey(lookupKey)) {
        return lookup.get(lookupKey);
    }


    int count = 0;
    for (int i = begin + 1; i <= end; i = i + 2) {
        int leftBegin = begin;
        int leftEnd = i - 1;
        int rightBegin = i + 1;
        int rightEnd = end;

        switch (expr.charAt(i)) {
            case '|':
                if (result) {
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                } else {
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                }

                break;
            case '&':
                if (result) {
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                } else {
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                }

                break;
            case '^':
                if (result) {
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                } else {
                    count += countParenthesization(expr, leftBegin, leftEnd, true, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, true, lookup);
                    count += countParenthesization(expr, leftBegin, leftEnd, false, lookup)
                            * countParenthesization(expr, rightBegin, rightEnd, false, lookup);
                }

                break;
        }
    }

    lookup.put(lookupKey, count);

    //System.out.println(lookup);

    return count;
}

【问题讨论】:

  • 我对当前的答案不满意。有人能帮忙详细说明一下上面代码的运行时间是什么吗?

标签: algorithm recursion runtime memoization boolean-expression


【解决方案1】:

正如所写,您的代码为 O(n^4)。该代码本质上与 DP 解决方案相同,但 DP 解决方案谨慎地使用 O(1) 索引到表中(一对 (i, j) 整数),此代码使用子字符串,构造其中需要 O(n) 时间,在哈希表中查找也需要 O(n) 时间。 [注意:这里的n指的是当前被分片的表达式字符串的长度,而不是哈希表的大小]。

您可以通过使用开始/结束索引并避免字符串切片(和哈希表查找)来弥补增加的复杂性,就像在 DP 解决方案中一样。

具体来说:

  • 该函数将采用额外的参数int i, int j 的开始/结束索引而不是预切片表达式。
  • 您的 lookup 哈希表的键将更改为元组 &lt;int i, int j, bool result&gt; 而不是字符串。
  • 您的代码根本不会执行任何字符串切片。

【讨论】:

  • 嗨@PaulHankin,感谢您的回答。你说运行时间是 O(n^4)。你能详细说明它是O(n ^ 4)吗?想象一下,您不知道运行时间为 n^3 的 DP 解决方案。换句话说,当你有三个循环时,很容易想出 n^3。但是,当您有一个包含 3 个带有记忆功能的递归调用的循环时,最好的分析方法是什么?
  • 给定一个表达式,假设子结果已经计算和散列,然后分析代码的复杂性——(它是 O(n^2),或者优化后的 O(n)在答案中建议)。然后注意,表达式的每个子字符串(在最坏的情况下)最终都会被计算出来,并且有 O(n^2) 个子字符串,所以总体复杂度是 O(n^4)。本质上,您必须像在数学证明中那样进行辩论,而不是进行简单的基于循环的复杂性分析。当然,这个评论是非正式的和暗示性的,而不是仔细和数学上的精确。
  • 这个论点本质上是在自顶向下递归记忆解决方案和自底向上 DP 解决方案之间构建等价。如果您忽略|&amp; 而只考虑^,则该论点最有效——因为自上而下的解决方案可以缩短|&amp; 情况下的一些工作。但最坏情况下的行为保持不变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-16
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 2017-01-25
  • 2021-06-23
  • 2021-02-17
相关资源
最近更新 更多