Différence entre récursion et itération

Un programme est dit récursif lorsqu’une entité s’appelle elle-même. Un programme est appelé itératif lorsqu’il y a une boucle (ou répétition).

Exemple : Programme pour trouver la factorielle d’un nombre

Vous trouverez ci-dessous des explications détaillées pour illustrer la différence entre les deux :

Pour un aperçu, 👉 cliquez sur une couverture pour feuilleter le livre sur Amazon 📚.


1 – Complexité du temps : Trouver la complexité temporelle de la récursion est plus difficile que celle de l’itération.

  • Récursion : La complexité temporelle de la récursivité peut être trouvée en trouvant la valeur du n-ième appel récursif par rapport aux appels précédents. Ainsi, trouver le cas de destination en termes de cas de base, et le résoudre en termes de cas de base nous donne une idée de la complexité temporelle des équations récursives.
  • Itération : La complexité temporelle de l’itération peut être trouvée en trouvant le nombre de cycles qui se répètent à l’intérieur de la boucle.

2 – Utilisation : L’utilisation de l’une ou l’autre de ces techniques est un compromis entre la complexité temporelle et la taille du code. Si la complexité temporelle est le point central, et que le nombre d’appels récursifs est important, il est préférable d’utiliser l’itération. Cependant, si la complexité temporelle n’est pas un problème et que la taille du code l’est, la récursivité serait la solution.

  • Récursion : La récursivité consiste à appeler la même fonction à nouveau, et donc, a une très petite longueur de code. Cependant, comme nous l’avons vu dans l’analyse, la complexité temporelle de la récursion peut devenir exponentielle lorsqu’il y a un nombre considérable d’appels récursifs. Par conséquent, l’utilisation de la récursion est avantageuse dans le cas d’un code plus court, mais d’une complexité temporelle plus élevée.
    Itération : L’itération est la répétition d’un bloc de code. Cela implique une plus grande taille de code, mais la complexité temporelle est généralement moindre que pour la récursivité.

3 – Répétition infinie : La répétition infinie en récursion peut entraîner un crash du processeur mais en itération, elle s’arrête lorsque la mémoire est épuisée.

  • Récursion : Dans la récursion, des appels récursifs infinis peuvent se produire en raison d’une erreur de spécification de la condition de base, qui, ne devenant jamais fausse, continue d’appeler la fonction, ce qui peut entraîner un crash du CPU du système.
  • Itération : L’itération infinie due à une erreur dans l’affectation ou l’incrémentation de l’itérateur, ou dans la condition de fin, conduira à des boucles infinies, qui peuvent ou non conduire à des erreurs système, mais arrêteront sûrement l’exécution du programme plus loin.
PROPRIÉTÉ RÉCURSION ITÉRATION
Définition La fonction s’appelle elle-même. Un ensemble d’instructions exécutées de manière répétitive.
Application Pour les fonctions. Pour les boucles.
Terminaison Par le cas de base, où il n’y aura pas d’appel de fonction. Lorsque la condition de sortie de l’itérateur cesse d’être remplie.
Utilisation Utilisé lorsque la taille du code doit être petite et que la complexité du temps ne pose pas de problème. Utilisé lorsque la complexité temporelle doit être compensée par une taille de code plus importante.
Taille du code Taille du code plus petite Taille du code plus grande.
Complexité temporelle Très grande (généralement exponentielle) complexité temporelle. Complexité temporelle relativement plus faible (généralement polynomiale et logarithmique).

Comprendre les fonctions récursives avec Python

Introduction

Lorsque nous pensons à répéter une tâche, nous pensons généralement aux boucles For et While. Ces constructions nous permettent d’effectuer des itérations sur une liste, une collection, etc.

Cependant, il existe une autre forme de répétition d’une tâche, d’une manière légèrement différente. En appelant une fonction sur elle-même, pour résoudre une petite instance du même problème, nous effectuons une récursion.

Ces fonctions s’appellent elles-mêmes jusqu’à ce que le problème soit résolu, divisant pratiquement le problème initial en de nombreuses petites instances de lui-même; comme par exemple, prendre de petites bouchées d’un plus gros morceau de nourriture.

L’objectif final est de manger toute l’assiette. Vous le faites en prenant une bouchée encore et encore. Chaque bouchée est une action récursive, après laquelle vous entreprenez la même action la fois suivante. Vous faites cela pour chaque bouchée, en évaluant que vous devez en prendre une autre pour atteindre le but, jusqu’à ce qu’il n’y ait plus de nourriture dans votre assiette.

Pour un aperçu, 👉 cliquez sur une couverture pour feuilleter le livre sur Amazon 📚.


Qu’est-ce que la récursion ?


Comme indiqué dans l’introduction, la récursion implique un processus qui s’appelle lui-même par définition. Une fonction récursive comporte généralement deux composantes :

  • Le cas de base, qui est une condition qui détermine quand la fonction récursive doit s’arrêter
  • L’appel à soi-même

Prenons un petit exemple pour démontrer les deux composantes :

Le cas de base pour nous est que si la variable i est égale à 0, c’est-à-dire combien de chaînes “Bonjour !” restantes nous devons imprimer. La fonction s’arrêtera tout simplement.

Après l’instruction print, nous appelons à nouveau bonjour_recursive mais avec une valeur i réduite. C’est important ! Si nous ne réduisons pas la valeur restante, la fonction sera exécutée indéfiniment. En général, lorsqu’une fonction récursive s’appelle elle-même, les paramètres sont modifiés pour être plus proches du cas de base.

Visualisons comment cela fonctionne lorsque nous appelons bonjour_recursive(5) :

Après que la fonction ait affiché “Bonjour !”, elle s’appelle elle-même avec une valeur inférieure pour rester jusqu’à ce qu’elle atteigne 0. À zéro, la fonction retourne à l’endroit où elle a été appelée dans bonjour_recursive(1), qui retourne à l’endroit où elle a été appelée dans bonjour_recursive(2) … et qui retourne finalement à l’endroit où elle a été appelée dans bonjour_recursive(5).

Pourquoi ne pas utiliser une boucle ?

Toutes les opérations peuvent être effectuées à l’aide de boucles. Néanmoins, certains problèmes sont souvent plus faciles à résoudre avec la récursion plutôt qu’avec l’itération. Un cas d’utilisation courant de la récursion est le parcours d’un arbre :

Il est généralement plus facile de penser à la traversée des nœuds et des branches d’un arbre en utilisant la récursion. Même si les boucles et la récursivité parcourent toutes les deux l’arbre, elles ont des objectifs différents : les boucles sont destinées à répéter une tâche alors que la récursivité est destinée à décomposer une grande tâche en tâches plus petites.

La récursion avec les arbres par exemple fonctionne bien parce que nous pouvons traiter l’arbre entier en traitant individuellement des parties plus petites de l’arbre.

Exemples

La meilleure façon de se familiariser avec la récursion, ou tout autre concept de programmation, est de la pratiquer. La création de fonctions récursives est simple : veillez à inclure dans votre programme votre cas de base et à appeler la fonction de manière à ce qu’elle se rapproche du cas de base.

Somme d’une liste

Python inclut une fonction de somme pour les listes. L’implémentation par défaut de Python, utilise une boucle de for-loop en C pour créer cette fonction.

Voyons comment faire avec la récursion :

Le cas de base est la liste vide – la meilleure somme pour cela est 0. Une fois que nous avons traité notre cas de base, nous supprimons le dernier élément de la liste. Nous appelons enfin la fonction sum_recursive avec la liste réduite, et nous ajoutons le nombre que nous avons retiré au total.

Conclusion

La récursivité nous permet de décomposer une grande tâche en de plus petites en s’appelant de façon répétitive. Une fonction récursive nécessite un cas de base pour arrêter l’exécution, et l’appel à soi-même qui conduit progressivement à la fonction au cas de base. Elle est couramment utilisée dans les arbres, mais d’autres fonctions peuvent être écrites avec la récursivité pour fournir des solutions élégantes.

Comment insérer des éléments dans une liste chainée en Python ?

Insertion des éléments

Selon l’endroit où vous souhaitez insérer un élément, il existe différentes façons d’insérer des éléments dans une seule liste chaînée

.Insertion d’éléments au début

La façon la plus simple d’insérer un élément dans une liste chaînée est d’ajouter un élément au début de la liste. La fonction suivante permet d’insérer un élément au début de la liste. Ajoutez cette fonction à la classe LinkedList que nous avons créée précédemment.

    def insert_at_start(self, data):
        new_node = Node(data)
        new_node.ref = self.start_node
        self.start_node= new_node

Dans le script ci-dessus, nous créons une méthode insert_at_start(), la méthode accepte un paramètre, qui est essentiellement la valeur de l’élément que nous voulons insérer. À l’intérieur de la méthode, nous créons simplement un objet de la classe Node et nous fixons sa référence au start_node puisque start_node stockait auparavant le premier nœud, qui après l’insertion d’un nouveau nœud au départ deviendra le deuxième nœud.

Par conséquent, nous ajoutons la référence de start_node à la variable ref du nouveau nœud. Maintenant, puisque new_node est le premier nœud, nous fixons la valeur de la variable start_node à new_node.

Insertion d’éléments à la fin

La fonction suivante est utilisée pour ajouter un élément à la fin de la liste chaînée.

  def insert_at_end(self, data):
        new_node = Node(data)
        if self.start_node is None:
            self.start_node = new_node
            return
        n = self.start_node
        while n.ref is not None:
            n= n.ref
        n.ref = new_node;

Pour un aperçu, 👉 cliquez sur une couverture pour feuilleter le livre sur Amazon 📚.


Dans le script ci-dessus, nous créons une fonction insert_at_end(), qui insère l’élément à la fin de la liste chaînée. La valeur de l’élément que nous voulons insérer est passée en argument à la fonction. La fonction se compose de deux parties. Tout d’abord, nous vérifions si la liste chaînée est vide ou non. Si la liste chaînée est vide, il suffit de fixer la valeur de la variable start_node à l’objet new_node.

D’autre part, si la liste contient déjà quelques nœuds. Nous initialisons une variable n avec le nœud de départ. Nous itérons ensuite à travers tous les nœuds de la liste en utilisant une boucle while comme nous l’avons fait dans le cas de la fonction traverse_list. La boucle se termine lorsque nous atteignons le dernier nœud. Nous fixons alors la référence du dernier nœud au nouveau new_node nouvellement créé.

Ajoutez la fonction insert_at_end() à la classe LinkedList.

Insertion d’un élément après un autre élément

Il se peut que nous devions ajouter un élément après l’autre dans une seule liste chaînée. Pour ce faire, nous pouvons utiliser la fonction insert_after_item() telle que définie ci-dessous :

    def insert_after_item(self, x, data):

        n = self.start_node
        print(n.ref)
        while n is not None:
            if n.item == x:
                break
            n = n.ref
        if n is None:
            print("Elément dans la liste")
        else:
            new_node = Node(data)
            new_node.ref = n.ref
            n.ref = new_node

La fonction insert_after_item() accepte deux paramètres : x et data. Le premier paramètre est l’élément après lequel vous voulez insérer le nouveau noeud tandis que le second paramètre contient la valeur du nouveau noeud.

Nous commençons par créer une nouvelle variable n et lui attribuer la variable start_node. Ensuite, nous parcourons la liste liée en utilisant la boucle while. La boucle while s’exécute jusqu’à ce que n devienne None. Au cours de chaque itération, nous vérifions si la valeur stockée dans le nœud actuel est égale à la valeur passée par le paramètre x. Si la comparaison est vraie, nous rompons la boucle.

Ensuite, si l’élément est trouvé, la variable n ne sera pas None. La référence du new_node est définie comme référence stockée par n et la référence de n est définie comme new_node. Ajoutez la fonction insert_after_item() à la classe LinkesList.

Insertion d’un élément avant un autre élément

      def insert_before_item(self, x, data):
        if self.start_node is None:
            print("La liste n'a pas d'élément.")
            return

        if x == self.start_node.item:
            new_node = Node(data)
            new_node.ref = self.start_node
            self.start_node = new_node
            return

        n = self.start_node
        print(n.ref)
        while n.ref is not None:
            if n.ref.item == x:
                break
            n = n.ref
        if n.ref is None:
            print("L'élément n'est pas dans la liste.")
        else:
            new_node = Node(data)
            new_node.ref = n.ref
            n.ref = new_node

Dans le script ci-dessus, nous définissons la fonction insert_before_item(). Cette fonction comporte trois parties. Examinons chaque partie en détail.

   if self.start_node is None:
        print("La liste n'a pas d'élément.")
        return

Dans le script ci-dessus, nous vérifions si la liste est vide. Si elle est effectivement vide, nous imprimons simplement que la liste n’a pas d’élément et nous revenons de la fonction.

Ensuite, nous vérifions si l’élément se trouve dans le premier index. Regardez le script suivant :

     if x == self.start_node.item:
        new_node = Node(data)
        new_node.ref = self.start_node
        self.start_node = new_node
        return

Si l’élément après lequel on veut insérer un nouveau nœud se trouve au premier index. Nous fixons simplement la référence du nœud nouvellement inséré au start_node, puis nous fixons la valeur de start_node à new_node.

Enfin, si la liste n’est pas None et que l’élément ne se trouve pas au premier index, nous créons une nouvelle variable n et lui attribuons la variable start_node. Ensuite, nous parcourons la liste liée en utilisant la boucle while. La boucle while-loop s’exécute jusqu’à ce que n.ref devienne None. Au cours de chaque itération, nous vérifions si la valeur stockée dans la référence du nœud courant est égale à la valeur passée par le paramètre x. Si la comparaison est vraie, nous cassons la boucle.

Ensuite, si l’élément est trouvé, la variable n.ref ne sera pas None. La référence du nouveau_nœud est fixée à la référence de n et la référence de n est fixée à new_node. Regardez le script suivant :

    if n.ref is None:
        print("L'élément n'est pas dans la liste.")
    else:
        new_node = Node(data)
        new_node.ref = n.ref
        n.ref = new_node

Ajoutez la fonction insert_before_item() à la classe LinkedList.

Insertion d’un élément à un index spécifique

Parfois, nous avons besoin d’insérer un élément à un index spécifique, nous pouvons le faire à l’aide du script suivant :

    def insert_at_index (self, index, data):
        if index == 1:
            new_node = Node(data)
            new_node.ref = self.start_node
            self.start_node = new_node
        i = 1
        n = self.start_node
        while i < index-1 and n is not None:
            n = n.ref
            i = i+1
        if n is None:
            print("Index out of bound")
        else: 
            new_node = Node(data)
            new_node.ref = n.ref
            n.ref = new_node

Dans le script, nous vérifions d’abord si l’index dans lequel nous voulons stocker l’élément est 1, puis nous attribuons simplement start_node à la référence du new_node et nous fixons ensuite la valeur de start_node à new_node.

Ensuite, on exécute une boucle while qui s’exécute jusqu’à ce que le compteur i devienne supérieur ou égal à l’index-1. Par exemple, si vous voulez ajouter un nouveau nœud au troisième index. Au cours de la première itération de la boucle while, i deviendra 2 et le nœud actuellement itéré sera “2”. La boucle ne s’exécutera pas à nouveau puisque i est maintenant 2, ce qui est égal à l’index 1 (3-1=2). Par conséquent, la boucle sera interrompue. Ensuite, nous ajoutons un nouveau nœud après le nœud actuellement itéré (qui est le nœud 2), ce qui fait que le nouveau nœud est ajouté à l’index.

Il est important de mentionner que si l’index ou l’emplacement passé en argument est supérieur à la taille de la liste liée, un message sera affiché à l’utilisateur indiquant que l’index est hors de portée ou hors limite.

Il ne vous reste plus qu’à tester vos fonctions d’insertion en Python.

Comment ajouter des éléments à une liste en Python?

En bref, une liste est une collection d’objets arbitraires, un peu comme un tableau dans de nombreux autres langages de programmation, mais plus flexible. Les listes sont définies en Python en mettant entre crochets ([]) une séquence d’objets séparés par des virgules, comme indiqué ci-dessous :

Les caractéristiques importantes des listes Python sont les suivantes :

  • Les listes sont ordonnées.
  • Les listes peuvent contenir n’importe quel objet arbitraire.
  • Les éléments des listes sont accessibles par index.
  • Les listes peuvent être imbriquées à une profondeur arbitraire.
  • Les listes sont modifiables.
  • Les listes sont dynamiques.

Pour ajouter des éléments ou item à une liste en Python, il existe 3 grandes méthodes.

Utilisation de la méthode append()

Les éléments peuvent être ajoutés à la liste en utilisant la fonction append() intégrée. Un seul élément à la fois peut être ajouté à la liste en utilisant la méthode append(), pour l’ajout de plusieurs éléments avec la méthode append(), des boucles sont utilisées. Les tuples peuvent également être ajoutés à la liste en utilisant la méthode append() car les tuples sont immuables. Contrairement aux Sets, les Listes peuvent également être ajoutées à la liste existante avec l’utilisation de la méthode append().

# Ajout d'éléments dans une liste  
  
# Créer une liste
List = [] 
print("Liste initiale vierge : ") 
print(List) 
  
# Ajout d'éléments dans la liste 
List.append(1) 
List.append(2) 
List.append(4) 
print("\nListe après l'ajout de trois éléments : ") 
print(List) 
  
# Ajout d'éléments à la liste en utilisant l'itérateur 
for i in range(1, 4): 
    List.append(i) 
print("\nListe après l'ajout d'éléments de 1-3: ") 
print(List) 
  
# Ajout de tuples à la liste 
List.append((5, 6)) 
print("\nListe après l'ajout d'un n-uplet : ") 
print(List) 
  
# Ajout d'une liste à une liste  
List2 = ['Pour', 'Geeks'] 
List.append(List2) 
print("\nListe après l'ajout d'une liste : ") 
print(List) 

Utilisation de la méthode insert()

La méthode append() ne fonctionne que pour l’ajout d’éléments à la fin de la liste, pour l’ajout d’un élément à la position souhaitée, la méthode insert() est utilisée. Contrairement à append() qui ne prend qu’un seul argument, la méthode insert() nécessite deux arguments (position, valeur).

# Ajout d'éléments dans une liste 
   
# Créer une liste 
List = [1,2,3,4] 
print("Liste initiale :  ") 
print(List) 
  
# Ajout d'un élément à position spécifique (en utilisant la méthode d'insertion)  
List.insert(3, 12) 
List.insert(0, 'Geeks') 
print("\nListe après avoir effectué l'opération d'insertion : ") 
print(List) 

Utilisation de la méthode extend()

Outre les méthodes append() et insert(), il existe une autre méthode pour l’ajout d’éléments, extend(), cette méthode est utilisée pour ajouter plusieurs éléments en même temps à la fin de la liste.

# Ajout d'éléments dans une liste 
    
# Créer une liste 
List = [1,2,3,4] 
print("Liste initiale : ") 
print(List) 
  
# Ajout d'éléments multiples à la liste à la fin (en utilisant la méthode Extend) 
List.extend([8, 'Geeks', 'Toujours']) 
print("\nListe après avoir effectué l'opération Extend Operation: ") 
print(List) 

Pour un aperçu, 👉 cliquez sur une couverture pour feuilleter le livre sur Amazon 📚.


Pour aller plus loin

Comment créer une liste en Python ?

Les listes sont comme des tableaux de taille dynamique, déclarés dans d’autres langages (vectoriel en C++ et ArrayList en Java). Les listes n’ont pas besoin d’être toujours homogènes, ce qui en fait un outil très puissant en Python. Une seule liste peut contenir des DataTypes comme des Entiers, des Chaînes, ainsi que des Objets. Les listes sont mutables, et donc, elles peuvent être modifiées même après leur création.

Les listes en Python sont ordonnées et ont un nombre défini. Les éléments d’une liste sont indexés selon une séquence définie et l’indexation d’une liste se fait avec 0 étant le premier index. Chaque élément de la liste a sa place définie dans la liste, ce qui permet de dupliquer les éléments de la liste, chaque élément ayant sa place et sa légitimité propres.

Création d’une liste

Les listes en Python peuvent être créées en plaçant simplement la séquence entre les crochets []. Contrairement aux Sets, les listes n’ont pas besoin de fonction intégrée pour la création de listes.

# Programmes de démonstration Python 
# Création de la liste  
  
# Création d'une liste 
List = [] 
print("Liste vide: ") 
print(List) 
  
# Création d'une liste avec des nombres 
List = [10, 20, 14] 
print("\nListe de nombres: ") 
print(List) 
  
# Créer une liste de chaînes et accéder 
# aux items avec l'index 
List = ["I", "love", "Python"] 
print("\nListe des iItems: ") 
print(List[0])  
print(List[2]) 
  
# Créer une liste multidimensionnelle 
# (En emboîtant une liste dans une liste) 
List = [['I', 'love'] , ['Python']] 
print("\nliste multidimensionnelle : ") 
print(List) 

Python: Debugger simplement avec l’IDE Tommy

Pour un aperçu, 👉 cliquez sur une couverture pour feuilleter le livre sur Amazon 📚.


Variables sans tracas.

Une fois que vous avez terminé avec code, sélectionnez Affichage → Variables et voyez comment vos programmes et commandes shell affectent les variables Python.

Variables table

Débogueur simple.

Appuyez simplement sur Ctrl + F5 au lieu de F5 et vous pouvez exécuter vos programmes étape par étape, aucun point d’arrêt n’est nécessaire. Appuyez sur F6 pour un grand pas et F7 pour un petit pas. Les étapes suivent la structure du programme, pas seulement les lignes de code.

Stepping through statements

Parcourez l’évaluation des expressions.

Si vous utilisez de petites étapes, vous pouvez même voir comment Python évalue vos expressions. Vous pouvez considérer cette boîte bleu clair comme un morceau de papier où Python remplace les sous-expressions par leurs valeurs, pièce par pièce.

Visualization of expression evaluation

Représentation fidèle des appels de fonction.

Entrer dans un appel de fonction ouvre une nouvelle fenêtre avec une table de variables locales et un pointeur de code séparés. Une bonne compréhension du fonctionnement des appels de fonction est particulièrement importante pour comprendre la récursivité.

Visualization of call frames

Met en évidence les erreurs de syntaxe.

Les guillemets et parenthèses non fermés sont les erreurs de syntaxe les plus courantes des débutants. L’éditeur de Thonny les rend faciles à repérer.

Visualization of syntax errors

Explique les portées de variable.

La mise en évidence des occurrences de variables vous rappelle que le même nom ne signifie pas toujours la même variable et aide à repérer les fautes de frappe. Les variables locales se distinguent visuellement des globales.

Local and global names are visually distinguished

Mode d’explication des références.

Les variables sont initialement présentées selon un modèle simplifié (nom → valeur) mais vous pouvez passer à un modèle plus réaliste (nom → adresse / id → valeur).

Variables table vs values table

Pour aller plus loin

IDE Tommy

 

Pour aller plus loin

Show Buttons
Hide Buttons
Translate »