Exercice — renverser une chaîne, puis reconnaître un palindrome
Une chaîne ch ne contient que des lettres minuscules. On veut écrire, sans aucune boucle, deux fonctions récursives : Renv(ch) qui retourne la chaîne renversée, et Pal(ch) qui retourne Vrai si la chaîne se lit de la même façon dans les deux sens (palindrome), Faux sinon.
Notations imposées : long(ch) est la longueur de la chaîne ; ch[i] est le caractère de rang i, les rangs allant de 0 à long(ch) - 1 ; sous(ch, i, k) retourne la portion de longueur k qui commence au rang i ; le signe + concatène deux chaînes ; la chaîne vide s'écrit "".
- Écrire la relation de récurrence de
Renv: ce que retourne le cas d'arrêt pour la chaîne vide, et commentRenv(ch)se ramène à un seul appel récursif sur une chaîne plus courte. Donner l'ordre de cette récurrence et nommer la grandeur qui décroît à chaque appel. - Écrire
Renven pseudo-code, puis son équivalent en Python. - Donner la trace des appels de
Renv("bac")jusqu'au cas d'arrêt, puis les valeurs retournées en remontant. - Une élève teste le palindrome par
Pal(ch) = (ch = Renv(ch)). Un élève propose au contraire de comparer le caractère de rang0et celui de ranglong(ch) - 1, puis de recommencer sur ce qui reste entre les deux. Écrire la fonction récursivePalde cette seconde méthode, et dire laquelle des deux méthodes donne sa réponse après le moins d'appels sur la chaîne"abcdefgh". - Cas limites : que doivent retourner
Pal("")etPal("r")? Expliquer pourquoi la seconde méthode réclame deux cas d'arrêt alors queRenvn'en demande qu'un. - La récurrence de la factorielle porte sur un entier. Dire ce qui joue, pour
Renv, le rôle den - 1, et ce qui joue le rôle du casn = 0.
Voir la correction commentéeAprès avoir posé votre démarche
Idée : sur une chaîne, la récursivité d'ordre 1 se lit toujours de la même façon — on détache un caractère, on relance la fonction sur ce qui reste, et on recolle. Tout le travail consiste à dire quel caractère on détache et de quel côté on le recolle.
1. Relation de récurrence de Renv. Cas d'arrêt : Renv("") = "" — renverser une chaîne vide la laisse vide. Cas général, pour long(ch) ≥ 1 : on détache le caractère de rang 0 et on le recolle à la fin du renversé de ce qui reste :
Renv(ch) = Renv(sous(ch, 1, long(ch) - 1)) + ch[0]
Un seul appel récursif figure dans cette égalité : la récurrence est d'ordre 1. La grandeur qui décroît est la longueur de la chaîne, qui perd exactement 1 à chaque appel ; partant d'une longueur finie, elle atteint 0 en long(ch) appels, donc l'arrêt est garanti.
2. Écriture. TDOL : ch de type chaîne ; aucun objet local.
DEF FN Renv(ch : chaîne) : chaîne
Début
Si ch = "" alors
Retourner ""
Sinon
Retourner Renv(sous(ch, 1, long(ch) - 1)) + ch[0]
Fin Si
Fin Renvdef renv(ch):
if ch == "":
return ""
return renv(ch[1:]) + ch[0]
print(renv("bac")) # cab3. Trace de Renv("bac"). Descente :
Renv("bac")→ attendRenv("ac") + "b"Renv("ac")→ attendRenv("c") + "a"Renv("c")→ attendRenv("") + "c"Renv("")→ cas d'arrêt, retourne""
Remontée : Renv("c") = "" + "c" = "c", puis Renv("ac") = "c" + "a" = "ca", puis Renv("bac") = "ca" + "b" = "cab". Quatre appels en tout, cas d'arrêt compris.
4. Palindrome par les extrémités. On compare les deux bouts, puis on recommence sur l'intérieur ; la longueur perd 2 à chaque appel.
DEF FN Pal(ch : chaîne) : booléen
Début
Si long(ch) <= 1 alors
Retourner Vrai
Sinon Si ch[0] <> ch[long(ch) - 1] alors
Retourner Faux
Sinon
Retourner Pal(sous(ch, 1, long(ch) - 2))
Fin Si
Fin PalSur "abcdefgh" : la méthode des extrémités compare a et h, qui diffèrent, et retourne Faux dès le premier appel. La méthode ch = Renv(ch) doit d'abord construire le renversé en entier, soit 9 appels de Renv (8 descentes plus le cas d'arrêt), avant la moindre comparaison. La seconde méthode répond donc au plus tôt : elle peut échouer sur le premier caractère, alors que la première paie toujours le prix complet.
5. Cas limites. Pal("") retourne Vrai (rien à comparer) et Pal("r") retourne Vrai (un caractère unique est son propre miroir). Il faut deux cas d'arrêt parce que la longueur diminue de 2 : partant d'une longueur paire elle atteint 0, partant d'une longueur impaire elle atteint 1. Un seul test long(ch) = 0 laisserait les chaînes de longueur impaire appeler sous(ch, 1, -1), c'est-à-dire une longueur négative. Renv, dont la longueur diminue de 1, passe forcément par 0 : un seul cas d'arrêt lui suffit.
6. Transposition depuis la factorielle. Le rôle de n - 1 est tenu par sous(ch, 1, long(ch) - 1), la chaîne privée de son premier caractère : c'est l'argument réduit d'un cran. Le rôle du cas n = 0 est tenu par la chaîne vide, valeur à partir de laquelle plus rien ne se réduit. La multiplication n × Fact(n-1) devient la concaténation ; l'élément neutre 1 devient la chaîne vide.