2010-04-27 4 views
5

J'ai ce programmeAccélérer une fonction avec la programmation dynamique

//h is our N 
    static int g=0; 
    int fun(int h){ 
     if(h<=0){ 
        g++; 
        return g; 
        } 
    return g+fun(h-1)+fun(h-4); 
    } 

Est-il possible de l'accélérer en utilisant la programmation dynamique?

Je compris que cette fonction fonctionne en O (2^n)

Je suis censé réduire le temps d'exécution par la programmation dynamique, mais ne comprennent pas le concept.

Juste demander un coup de pouce dans la bonne direction.

+0

Où est défini n? –

+0

@Michael Je pense que @aristotaly fait référence à cette notation de Big O thing-a-ma-jig. –

+1

n'a pas été retiré de la balise homework comme toutes les balises pseudo (inutiles par elles-mêmes pour classer une question)? – kriss

Répondre

7

Bien que je ne peux pas donner une réponse à votre question réelle, je suis intrigué par tout autre chose, à savoir la déclaration

return g+fun(h-1)+fun(n-4); 

De toute évidence, votre fonction a pour effet secondaire de modifier la variable statique globale g . Je ne suis pas sûr à 100% si l'expression de l'instruction return évalue réellement d'une manière clairement définie, ou si le résultat peut être indéfini.

Cela peut être un bon exercice de penser à l'ordre dans lequel ces appels de fonction sont exécutés, et comment cela affecte g et ainsi le résultat de la fonction.

+0

n'est-ce pas en quelque sorte la question? Le point entier de l'exercice semble être l'élimination de l'effet secondaire. – kriss

+0

@kriss: le problème est que, dans ce code, l'effet secondaire ne donne pas réellement un résultat bien défini. – jpalecek

+0

@jplacek: oui, il faut choisir un comportement entre les possibles avant de supprimer l'effet secondaire (et l'effet de la valeur initiale de g). Définir mathématiquement la fonction serait beaucoup mieux. – kriss

0

Oui, il est possible d'utiliser DP pour l'accélérer et éviter d'utiliser la pile CPU, mais je suis d'accord avec stakx sur les effets secondaires de la modification de la variable statique globale g. Il est préférable de fournir une expression mathématique car la fonction récursive ci-dessus peut donner un résultat différent en fonction de l'ordre & nombre d'appels.

1

Si nous définissons cet ordre de sommation dans g + fun (h-1) + fun (n-4) est de gauche à droite, que c'est un bon problème défini. Avec ce que je reçois des valeurs pour le plaisir (n), n = 1, ..., 15:

3, 6, 10, 15, 33, 74, 154, 295, 575, 1143, 2269, 4414, 8508, 16396, 31634 

Valeur de retour de plaisir (n) est évalué comme séquence de sommations avec des éléments non-descendants. Chaque somme est pour un plus grand que le précédent (return g ++;) ou identique au précédent (return g + fun() + fun()). La séquence d'exécution des instructions de retour dépend uniquement du paramètre d'entrée fun(). Donc, avec g mis à la valeur initiale! = 0 nous obtenons les mêmes invocations qu'avec g = 0, mais chaque somme est plus grande pour la même valeur initiale. Avec cela, le plaisir (n) avec g initiale> 0 retournera valeur est g * nombre de déclarations de retour exécutées plus grandes qu'avec initial g = 0.

Définir A (n) que le nombre de déclarations de retour exécutées lors de l'exécution du numéro fun (n) et G (n) de l'instruction return exécutée dans la clause si (identique au nombre d'exécutions d'instructions g ++). A et G détient:

A(n) = A(n-1) + A(n-4) + 1 
G(n) = G(n-1) + G(n-4) 
A(n) = 1 and G(n) = 1, for n <= 0 

A partir de ces observations, on peut voir que pour n> 0 est vérifiée:

fun(n) = fun(n-1) + G(n-1) * A(n-4) + fun(n-4) 

implémentation Python simple:

def x(h): 
    Rg = { -3:1, -2:1, -1:1, 0:1 } 
    Ra = { -3:1, -2:1, -1:1, 0:1 } 
    F = { -3:1, -2:1, -1:1, 0:1 } 
    for i in xrange(1, h+1): 
    F[i] = F[i-1] + Rg[i-1]*Ra[i-4] + F[i-4] 
    print i, F[i] 
    Rg[i] = Rg[i-1] + Rg[i-4] 
    Ra[i] = Ra[i-1] + Ra[i-4] + 1 

@stakx: pour l'expression g + fun (h-1) + fun (h-4) on ne peut pas avoir de garantie d'ordre d'évaluation, surtout pas en C.

+0

Si vous essayez la fonction C, même si l'ordre d'évaluation n'est pas garanti, vous pouvez voir que votre fonction python ne retourne pas les mêmes résultats. Il devrait y avoir une erreur quelque part dans votre analyse. Etes-vous heureux de retourner le nombre de déclarations de retour * g. Cela ne me semble pas évident. – kriss

+0

@kriss: en supposant que g + fun (n-1) + fun (n-4) est évalué de gauche à droite, que fun (1) = 0 + fun (0) + fun (-3) = 0 + 1 + 2 = 3. Quand je lance cette implémentation C, je m'amuse (1) = 4. Changement de ligne: return g + fun (h-1) + fun (h-4); avec des lignes: int aa = g; return aa + fun (h-1) + fun (h-4); Je m'amuse (1) = 3. La différence est probablement dans l'optimisation. – Ante

+0

oui. J'ai obtenu les mêmes résultats une fois que j'ai forcé l'ordre d'évaluation de gauche à droite. – kriss

0

OK. Nous partons du fun (une version sérialisée où l'ordre d'évaluation est forcé).

int fun(int h){ 
    if(h<=0){ 
     g++; 
     return g; 
    } 
    int tmp1 = g; 
    int tmp2 = fun(h-1); 
    int tmp3 = fun(h-4); 
    return tmp1+tmp2+tmp3; 
} 

Concentrons-sur g et oublier le résultat courant de la fonction

Maintenant, il est facile de changer la fonction de passer en g en tant que paramètre et retourner la nouvelle valeur de g en conséquence.

int gun(int h, int g0){ 
    if(h<=0){ 
     return g0+1; 
    } 
    int tmp1 = g0; 
    int tmp2 = gun(h-1, g0); 
    int tmp3 = gun(h-4, tmp2); 
    return tmp3; 
} 

What can be simplified into: 

int gun(int h, int g0){ 
    if(h<=0){ 
     return g0+1; 
    } 
    return gun(h-4, gun(h-1, g0)); 
} 

Revenons maintenant à l'amusement:

int fun2(int h, int g0){ 
    if(h<=0){ 
     return g0+1; 
    } 
    return g0+fun2(h-1, g0)+fun2(h-4, gun(h-1,g0)); 
} 

fun2 fait exactement la même chose que le plaisir initial, mais maintenant que nous avons supprimé l'effet secondaire et la fonction ne dépend que de son paramètre, nous pouvons memoize résultats (stocker les résultats déjà calculés dans un tableau), ce qui devrait accélérer le calcul.

Nous pouvons encore simplifier un peu le pistolet. Le paramètre G0 est pas vraiment nécessaire, Fixons à 0.

int gun2(int h){ 
    if(h<=0){ 
     return 1; 
    } 
    return gun2(h-4) + gun2(h-1); 
} 

On peut même définir un fun3 avec le paramètre G0 fixé à 0, désormais un peu plus simple, mais il doit encore appeler fun2. Je ne vois pas encore comment simplifier encore mais c'est probablement possible.

int fun3(int h){ 
    if(h<=0){ 
     return 1; 
    } 
    return fun3(h-1)+fun2(h-4, gun2(h-1)); 
} 
+1

dans la deuxième étape de réécriture, j'ai été quelque peu confus par 'int tmp3 = pistolet (h-4, tmp2);'. Pourquoi passez-vous 'tmp2' pour le paramètre' g'? De plus, puisque la fonction pourrait changer 'g', la valeur actuelle de' g' ne devrait-elle pas être retournée, c.-à-d. cette fonction ne devrait-elle pas retourner un (deux-) tuple? – stakx

+0

@jpalecek: pour l'accélération, une fois que nous avons de vraies fonctions sans effets secondaires, mémoriser (garder des valeurs déjà calculées) est facile (même si cela peut prendre un peu de place). Je ne comprends pas de quoi tu parles quand tu dis "ça ne marche pas". Ma fonction de pistolet n'est pas censée calculer la même chose que du plaisir, mais juste pour retourner la même valeur pour g que ce qu'elle aurait après l'avoir mise à 0 et avoir appelé le plaisir initial avec des effets secondaires. pistolet ne reviendra pas clairement la même valeur que l'amusement. Mais vous parlez peut-être d'autre chose? J'ai peut-être fait une erreur. – kriss