Récursivité
Quelques exercices pour commencer
Donner une implantation récursive pour chacune des fonctions suivantes.
Génération récursive
La récursion est particulièrement utile lorsqu'il s'agit de générer récursivement des listes d'objets. Par exemple, je veux une fonction qui puisse me retourner la liste des mots binaires d'une taille donnée, c'est à dire la liste de toutes les chaînes de caractères de taille n contenant uniquement les lettres "0" ou "1". Pour , une telle fonction me retournerait l'objet suivant :
Voici les fonctions qui générent les mots de taille 0, 1 et 2 :
Sur le même modèle, écrivez une fonction getBinaire3.
Maintenant que vous avez compris le principe, écrivez une fonction qui génère les mots binaires de taille n. Remarque : votre fonction doit être all inclusive, n'utilisez pas les fonctions getBinaireX écrites précédemment.
Remarque pour ceux qui connaissent bien python : les tests ont été écrits pour passer aussi si vous retourner un générateur (avec yield) au lieu d'une liste.
Sur le même modèle, implanter la fonction suivante. (un peu plus dur)
Posez-vous la question de cette façon : si j'ai un mot de taille n qui termine par 0 et qui contient k 1, combien de 1 contenait le mot taille n-1 à partir duquel il a été créé ? De même s'il termine par 1.
Et pour finir (si vous ne voyez pas comment faire, passez à la suite...)
Exécutez la ligne suivante et copiez la liste de nombre obtenu dans google, vous pouvez vous instruire en lisant les résultat. Êtes vous capable d'écrire un programme qui calcule ces nombres plus rapidement ?
Le problème du porte monnaie
Le problème est le suivant : je possède un certain ensemble de pièces , puis-je rendre la somme en utilisant mes pièces ?
Par exemple, si je possède les pièces , je peux rendre les sommes 9 et 14 mais pas 8 et 15. Notez que représente exactement les pièces que j'ai et pas uniquement la valeur de mes pièces (je ne peux pas utiliser une 3ème pièce 5 pour rendre le nombre 15)
Ce problème est typiquement récursif.
On cherche à implanter la fonction rendMonnaie qui prend en paramètre une liste de pièces et la valeur à rendre. Lire d'abord l'aide et les rappels syntaxiques
Aide : le principe de la récursion est le suivant, on choisis une pièce de et on répond aux deux questions suivantes
puis-je rendre la monnaie en utilisant ? Si oui, alors je peux rendre la monnaie
Si non, puis-je rendre la monnaie sans utiliser ?
Si aucune des deux possibilités n'est vérifiée, alors je ne peux pas rendre la monnaie.
Réfléchissez aux questions suivantes puis implantez l'algorithme.
Que signifie le cas ? que dois-je répondre ?
Que signigifie le cas où et est vide ? que dois-je répondre ?
Si je ne suis dans aucun de ces deux cas alors : et n'est pas vide. Je peux choisir une pièce de et tester 1. et 2., à chaque fois, quelles seront les nouvelles valeurs de et ?
** Rappels sur la syntaxe des listes ** (cf ci-dessous)
Attention : lorsqu'une une liste est passée en paramètre d'une fonction et modifiée la liste d'origine est aussi modifiée ! (passage par référence)
** La fonction à implanter : **
On adapte un peu la fonction de départ car on veut récupérer la solution sous la forme de la liste des pièces utilisées. Implanter la variante ci-dessous.
Combien d'appels récursifs sont nécessaires pour résoudre le problème ? Utiliser une variable globale et modifiez la fonction précédente pour compter le nombre d'appels lors des différents exemples. (pour utiliser une variable globale dans une fonction, la charger par l'instruction "global ")
Quelle est la complexité de l'algorithme dans le pire des cas ? Pour écrire votre réponse, sélectionner le mode Markdown dans le ménu déroulant lorsque vous cliquez sur une cellule.
On cherche à présent à retourner une solution optimale, c'est-à-dire en utilisant le moins de pièces possible. Pour cela, on utilise d'abord un algorithme dit glouton qui choisit sytématiquement la plus grande pièce et vérfie s'il existe une solution. Si c'est le cas, on ne teste pas les autres solutions.
Implanter l'algorithme glouton
Malheureusement, l'algorithme glouton ne donne pas toujours la solution optimale. Observer l'exemple ci-dessous.
Pour obtenir une solution optimale à tous les coups, on doit choisir une pièce et tester la taille de la solution avec p et sans p et retourner la meilleure des deux.
Implanter l'algorithme rendreMonnaieOptimal ci-dessous.
Quelques questions bonus :
Traiter le cas où les pièces sont données comme un ensemble de valeurs et où l'on peut donc réutiliser plusieurs fois les mêmes valeurs.
Peut-être avez vous remarqué que vos algorithmes font les mêmes calculs plusieurs fois, en les oubliant au fur et à mesure... Pas convaincu ? Faites un dessin de l'arbre des appels! Pour gérer ce problème il faut mémoriser les calculs intermédiaires, par exemple dans un tableau où l'entrée
[i][j]contiendraNoneou ce qui est retourné parvotre_fonction(i, j).Plus généralement, ce problème est un exemple typique d'une classe d'algorithmes que nous n'avons pas vu en cours et qu'on appelle programmation dynamique. Informez-vous sur la programmation dynamique et cet algorithme en particulier et essayez de l'implanter