Java 如何计算动态规划(记忆化)算法的大O
如何计算DP算法的大O。我逐渐意识到我的算法计算方法并不总是有效的。我会使用简单的技巧来提取大O是什么。例如,如果我在评估下面算法的无记忆版本(删除缓存机制),我会查看递归方法在本例中调用自身的次数3次。然后我将这个值提高到n,给出O(3^n)。对于DP,这一点都不正确,因为递归堆栈没有那么深。我的直觉告诉我,DP解的大O是O(n^3)我们如何口头解释我们是如何得出这个答案的更重要的是,什么是一种可以用来找出类似问题大O的技术。由于是DP,我确信子问题的数量很重要我们如何计算子问题的数量。Java 如何计算动态规划(记忆化)算法的大O,java,algorithm,big-o,dynamic-programming,Java,Algorithm,Big O,Dynamic Programming,如何计算DP算法的大O。我逐渐意识到我的算法计算方法并不总是有效的。我会使用简单的技巧来提取大O是什么。例如,如果我在评估下面算法的无记忆版本(删除缓存机制),我会查看递归方法在本例中调用自身的次数3次。然后我将这个值提高到n,给出O(3^n)。对于DP,这一点都不正确,因为递归堆栈没有那么深。我的直觉告诉我,DP解的大O是O(n^3)我们如何口头解释我们是如何得出这个答案的更重要的是,什么是一种可以用来找出类似问题大O的技术。由于是DP,我确信子问题的数量很重要我们如何计算子问题的数量。 pu
public class StairCase {
public int getPossibleStepCombination(int n) {
Integer[] memo = new Integer[n+1];
return getNumOfStepCombos(n, memo);
}
private int getNumOfStepCombos(int n, Integer[] memo) {
if(n < 0) return 0;
if(n == 0) return 1;
if(memo[n] != null) return memo[n];
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
return memo[n];
}
}
公共级楼梯{
公共整型getPossibleStepCombination(整型n){
整数[]备忘录=新整数[n+1];
返回getNumOfStepCombos(n,memo);
}
私有int getNumOfStepCombos(int n,整数[]备注){
如果(n<0)返回0;
如果(n==0)返回1;
如果(备注[n]!=null)返回备注[n];
memo[n]=getNumOfStepCombos(n-1,memo)+getNumOfStepCombos(n-2,memo)+getNumOfStepCombos(n-3,memo);
返回备忘录[n];
}
}
第一行3
除了比较int
值之外什么都不做,通过索引访问数组,查看整数
引用是否为null
。这些都是O(1)
,所以唯一的问题是该方法被递归调用了多少次
这个问题很复杂,所以我经常作弊。我只是用计数器看看发生了什么。(我已经为此将您的方法设置为静态,但一般来说,您应该尽可能避免静态可变状态)
因此,看起来最终计数器值由3n+1
给出
在一个更复杂的例子中,我可能无法识别模式,因此我将前几个数字(例如1、4、7、10、13、16)
输入到中,通常会看到一个包含模式简单公式的页面
一旦你通过这种方式作弊来找出规则,你就可以开始理解规则为什么起作用了
以下是我如何理解3n+1
的来源。对于n
的每个值,您只需执行以下操作
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
仅一次。这是因为我们正在记录结果,并且只有在尚未计算答案的情况下才执行这一行
因此,当我们从n==5
开始时,我们将该行精确地运行了5次;一次用于n==5
,一次用于n==4
,一次用于n==3
,一次用于n==2
,一次用于n==1
。这就是从自身调用方法getNumOfStepCombos
的次数3*5==15。该方法还从自身外部调用一次(从getPossibleStepCombination
),因此调用总数为3n+1
因此,这是一个O(n)
算法
如果一个算法的行不是O(1)
这个计数器方法不能直接使用,但是你可以经常采用这种方法。第一个3
行除了比较int
值之外什么都不做,通过索引访问数组,看看整数
引用是否为null
。这些都是O(1)
,所以唯一的问题是该方法被递归调用了多少次
这个问题很复杂,所以我经常作弊。我只是用计数器看看发生了什么。(我已经为此将您的方法设置为静态,但一般来说,您应该尽可能避免静态可变状态)
因此,看起来最终计数器值由3n+1
给出
在一个更复杂的例子中,我可能无法识别模式,因此我将前几个数字(例如1、4、7、10、13、16)
输入到中,通常会看到一个包含模式简单公式的页面
一旦你通过这种方式作弊来找出规则,你就可以开始理解规则为什么起作用了
以下是我如何理解3n+1
的来源。对于n
的每个值,您只需执行以下操作
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
仅一次。这是因为我们正在记录结果,并且只有在尚未计算答案的情况下才执行这一行
因此,当我们从n==5
开始时,我们将该行精确地运行了5次;一次用于n==5
,一次用于n==4
,一次用于n==3
,一次用于n==2
,一次用于n==1
。这就是从自身调用方法getNumOfStepCombos
的次数3*5==15。该方法还从自身外部调用一次(从getPossibleStepCombination
),因此调用总数为3n+1
因此,这是一个O(n)
算法
如果一个算法的行不是O(1)
,这个计数器方法不能直接使用,但你可以经常调整方法。保罗的答案在技术上没有错,但有点误导。我们应该通过函数如何响应输入大小的变化来计算大O符号。Paul对O(n)的回答使复杂性看起来是线性时间,而实际上它与表示n的位数成指数关系。例如,n=10有30次计算,m=2位。n=100有300次计算,m=3位。n=1000有约3000次计算,m=4位
我相信函数的复杂度是O(2^m),其中m是
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);