2017-08-19 3 views
1

Je ne suis pas bon à l'analyse d'algorithme. Le code source provient de cet endroit: https://repl.it/KREy/4Big O analyse de résoudre la plus longue sous-séquence palindromique par récursivité avec cache

Au lieu de la programmation dynamique, ce morceau de code utilise un cache pour optimiser le BigO en sacrifiant la mémoire. Cependant, je ne sais pas comment calculer mathématiquement le BigO après l'ajout de ce mécanisme de cache. Quelqu'un peut-il donner une explication?

Pour faciliter la lecture, je les copier et les coller dans l'espace suivant:

// 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(); 

Répondre

1

Je ne suis pas un expert en algorithmes non plus, mais je me souviens des techniques de cache comme celui-ci de l'introduction aux algorithmes, chapitre 15, juste à côté de la programmation dynamique. Il a le même gros O à DP, qui est O (n^2) dans votre cas.

Chaque appel à computeGetLPWithCache() coûte O (1) car il ne contient pas de boucles. Considérons le cas le plus défavorable où x [start]! = X [end] dans chaque récursivité. Combien de fois allons-nous appeler computeGetLPWithCache()? Soit n = longueur (x), [début, fin] représente un appel à computeGetLPWithCache (x, début, fin), et F (n) est égal au nombre d'appels. Dans computeGetLPWithCache (x, 0, n), 2 sous-appels - [0, n-1] et [1, n] - sont émis. Le premier coûte F (n), et quand nous faisons le dernier, nous découvrons qu'à chaque itération, la plage [début, fin] du premier appel est un vrai sous-ensemble de [0, n-1] dont le résultat est déjà écrit mettre en cache pendant l'appel [0, n-1], donc pas besoin de récursif. Seul le second appel qui contient l'élément n doit être calculé; il y a n appels de ce type [1, n] [2, n] [3, n] ... [n, n] (un dans chaque couche de pile), donc F (n + 1) = F (n) + Sur). 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).

J'espère que j'ai compris la signification. Les réponses sont les bienvenues.