带缓存的递归求解最长回文子序列的大O分析
Big O analysis of solving longest palindromic subsequence by recursion with cache
我不擅长算法分析。源代码来自这个地方:https://repl.it/KREy/4
这段代码不是动态规划,而是使用缓存通过牺牲内存来优化 BigO。但是,我就是不知道加了这个缓存机制之后,如何从数学上计算BigO。有哪位高手给个解释吗?
为了方便阅读,我将把它们复制粘贴到下面space:
// using cache to optimize the solution 1 from http://www.techiedelight.com/longest-palindromic-subsequence-using-dynamic-programming/
const cache = {};
var runningtime = 0;
var runningtimeWithCache = 0;
function computeGetLP(x, start, end){
const answer = a => {
runningtime++;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLP(x, start+1, end-1));
return answer(Math.max(computeGetLP(x, start+1, end),
computeGetLP(x, start, end-1)));
}
function computeGetLPWithCache(x, start, end){
const answer = a => {
runningtimeWithCache ++;
console.log("do cache: " + x + " " + start + " " + end + " is " + a);
cache["" + x + start + end] = a;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(cache["" + x + start + end]){
console.log("hit cache " + x + " " + start + " " + end + " "+ ": ",cache["" + x + start + end]);
return cache["" + x + start + end];
}
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLPWithCache(x, start+1, end-1));
return answer(Math.max(computeGetLPWithCache(x, start+1, end),
computeGetLPWithCache(x, start, end-1)));
}
const findLongestPadlindrom1 = s => computeGetLPWithCache(s, 0, s.length-1)
const findLongestPadlindrom2 = s => computeGetLP(s, 0, s.length-1)
const log = (function(){
var lg = [];
var output = function(text){
lg.push(text);
}
output.getRecord = function(){
return lg;
}
return output;
})();
log("Now let's do it with cache")
log("result: "+findLongestPadlindrom1("ABBDCACB"))
log("running time is: " + runningtimeWithCache)
log("Now let's do it without cache")
log("result: "+findLongestPadlindrom2("ABBDCACB"))
log("running time is: " + runningtime)
log.getRecord();
我也不是算法专家,但我记得算法导论第 15 章中的缓存技术,就在动态规划旁边。它对 DP 具有相同的大 O,在您的情况下为 O(n^2)。
每次调用 computeGetLPWithCache() 的成本为 O(1),因为它不包含循环。考虑每次递归中 x[start] != x[end] 的最坏情况。我们要调用多少次 computeGetLPWithCache()?
设n = length(x),[start, end]表示调用computeGetLPWithCache(x, start, end),F(n)等于调用次数。在 computeGetLPWithCache(x, 0, n) 中,发出了 2 个子调用 - [0, n-1] 和 [1, n]。前者花费 F(n),当我们做后者时,我们发现在每次迭代中,第一次调用的 [start, end] 范围是 [0, n-1] 的真实子集,其结果已经写入在 [0, n-1] 调用期间缓存,因此不需要递归。只需要计算其中包含元素 n 的第二次调用;有 n 个这样的调用 [1,n][2,n][3,n]...[n,n](每个堆栈层一个),所以 F(n+1) = F(n) + O(n).
F(n) = F(n-1) + O(n-1) = F(n-2) + O(n-2) + O(n-1) = ... = O (1+2+...+(n-1)) = O(n^2).
希望我已经理解了意思。欢迎回复。
我不擅长算法分析。源代码来自这个地方:https://repl.it/KREy/4
这段代码不是动态规划,而是使用缓存通过牺牲内存来优化 BigO。但是,我就是不知道加了这个缓存机制之后,如何从数学上计算BigO。有哪位高手给个解释吗?
为了方便阅读,我将把它们复制粘贴到下面space:
// using cache to optimize the solution 1 from http://www.techiedelight.com/longest-palindromic-subsequence-using-dynamic-programming/
const cache = {};
var runningtime = 0;
var runningtimeWithCache = 0;
function computeGetLP(x, start, end){
const answer = a => {
runningtime++;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLP(x, start+1, end-1));
return answer(Math.max(computeGetLP(x, start+1, end),
computeGetLP(x, start, end-1)));
}
function computeGetLPWithCache(x, start, end){
const answer = a => {
runningtimeWithCache ++;
console.log("do cache: " + x + " " + start + " " + end + " is " + a);
cache["" + x + start + end] = a;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(cache["" + x + start + end]){
console.log("hit cache " + x + " " + start + " " + end + " "+ ": ",cache["" + x + start + end]);
return cache["" + x + start + end];
}
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLPWithCache(x, start+1, end-1));
return answer(Math.max(computeGetLPWithCache(x, start+1, end),
computeGetLPWithCache(x, start, end-1)));
}
const findLongestPadlindrom1 = s => computeGetLPWithCache(s, 0, s.length-1)
const findLongestPadlindrom2 = s => computeGetLP(s, 0, s.length-1)
const log = (function(){
var lg = [];
var output = function(text){
lg.push(text);
}
output.getRecord = function(){
return lg;
}
return output;
})();
log("Now let's do it with cache")
log("result: "+findLongestPadlindrom1("ABBDCACB"))
log("running time is: " + runningtimeWithCache)
log("Now let's do it without cache")
log("result: "+findLongestPadlindrom2("ABBDCACB"))
log("running time is: " + runningtime)
log.getRecord();
我也不是算法专家,但我记得算法导论第 15 章中的缓存技术,就在动态规划旁边。它对 DP 具有相同的大 O,在您的情况下为 O(n^2)。
每次调用 computeGetLPWithCache() 的成本为 O(1),因为它不包含循环。考虑每次递归中 x[start] != x[end] 的最坏情况。我们要调用多少次 computeGetLPWithCache()?
设n = length(x),[start, end]表示调用computeGetLPWithCache(x, start, end),F(n)等于调用次数。在 computeGetLPWithCache(x, 0, n) 中,发出了 2 个子调用 - [0, n-1] 和 [1, n]。前者花费 F(n),当我们做后者时,我们发现在每次迭代中,第一次调用的 [start, end] 范围是 [0, n-1] 的真实子集,其结果已经写入在 [0, n-1] 调用期间缓存,因此不需要递归。只需要计算其中包含元素 n 的第二次调用;有 n 个这样的调用 [1,n][2,n][3,n]...[n,n](每个堆栈层一个),所以 F(n+1) = F(n) + O(n).
F(n) = F(n-1) + O(n-1) = F(n-2) + O(n-2) + O(n-1) = ... = O (1+2+...+(n-1)) = O(n^2).
希望我已经理解了意思。欢迎回复。