Exercices corrigés Python (complexité)

14 Feb 2017 14 Feb 2017 29921 vues ESSADDOUKI Mostafa

Exercices : Récursivité et complexité

Cette série d'exercices porte sur la récursivité et l'analyse de complexité des algorithmes. Vous serez amené à écrire des fonctions récursives pour résoudre divers problèmes et à estimer leur complexité temporelle.

Thèmes abordés Ces exercices couvrent les concepts fondamentaux de la récursivité : récursivité simple, récursivité mutuelle, diviser pour régner, et l'analyse de complexité par établissement de relations de récurrence.
 Exercice 1

Vérification de tableau trié

 Niveau : Débutant

Un tableau X est trié par ordre croissant si x(i) ≤ x(i+1) pour tout i.

  1. Élaborer un programme récursif permettant de vérifier qu'un tableau X est trié ou non.
  2. Estimer sa complexité.
 Exercice 2

Conversion en binaire

 Niveau : Intermédiaire

Pour convertir un nombre entier positif N de la base décimale à la base binaire, il faut opérer par des divisions successives du nombre N par 2. Les restes des divisions constituent la représentation binaire.

  1. Écrire une fonction récursive « Binaire » permettant d'imprimer à l'écran la représentation binaire d'un nombre N.
  2. Donner la formule récurrente exprimant sa complexité en nombre de divisions. Estimer cette complexité.
 Exercice 3

Suite de Fibonacci

 Niveau : Intermédiaire

La suite de Fibonacci est définie par :

  1. Écrire un programme récursif calculant Fib(n).
  2. Déterminer sa complexité.
 Exercice 4

Suite définie par récurrence

 Niveau : Intermédiaire

Soit la suite définie par :

  1. Écrire un programme récursif permettant de calculer le n-ième terme de la suite.
  2. Estimer sa complexité.
 Exercice 5

Récursivité mutuelle : Pair / Impair

 Niveau : Intermédiaire

Un nombre N est pair si (N-1) est impair, et un nombre N est impair si (N-1) est pair.

  1. Écrire deux fonctions récursives mutuelles pair(N) et impair(N) permettant de savoir si un nombre N est pair et si un nombre N est impair.
  2. Estimer la complexité de la fonction Pair(N) en nombre d'appels récursifs.
 Exercice 6

Maximum d'un tableau (diviser pour régner)

 Niveau : Avancé

Étant donné un tableau X composé de N éléments entiers. On voudrait déterminer son maximum par un programme récursif basé sur le paradigme « diviser pour régner » :

  1. En considérant que le maximum est le plus grand entre le dernier terme et le maximum des (n-1) premiers termes. Estimer sa complexité.
  2. En considérant que le maximum est le plus grand entre les maximums des deux moitiés du tableau. Estimer sa complexité.
 Exercice 7

Plus longue coupe de 1

 Niveau : Avancé

Soit un tableau X composé de N entiers pouvant être 0 ou 1. Une coupe (i,j) de X est le sous-tableau commençant à i et finissant à j. On voudrait déterminer la plus longue coupe ne contenant que des 1.

Exemple : dans le tableau ci-dessus, la coupe (7,14) est une coupe de 1.

  1. Écrire une fonction « PlusLongueCoupe » récursive basée sur le paradigme « diviser pour régner » qui détermine la plus longue coupe ne contenant que des 1 d'un tableau X.
  2. Estimer sa complexité moyenne en nombre de comparaisons des éléments du tableau.
 Exercice 8

Multiplication par additions successives

 Niveau : Avancé

On voudrait écrire une fonction « multiplier » permettant de multiplier deux entiers positifs ou nuls a et b en utilisant uniquement des opérations d'addition. Le prototype de la fonction doit être « def multiplier(a, b) ».

  1. Proposer une version récursive de la fonction « multiplier ». Estimer sa complexité en nombre d'additions après avoir déterminé une formule récurrente de la complexité.
  2. Proposer une autre version récursive de la fonction « multiplier » basée sur le paradigme « diviser pour régner ». Donner une formule récurrente de sa complexité en nombre d'additions et l'estimer.

Récapitulatif des complexités

ExerciceAlgorithmeComplexité
1 - Tableau triéVérification récursiveO(n)
2 - BinaireDivisions successivesO(log n)
3 - FibonacciRécursif naïf / mémoïsationO(φⁿ) / O(n)
4 - Suite définieRécursif naïf / mémoïsationO(φⁿ) / O(n)
5 - Pair/ImpairRécursivité mutuelleO(n)
6 - MaximumDiviser pour régnerO(n)
7 - Plus longue coupeDiviser pour régnerO(n log n)
8 - MultiplicationNaïve / DiviserO(b) / O(log b)
Points clés à retenir
  • La récursivitée permet d'exprimer élégamment des algorithmes mais peut être coûteuse en mémoire.
  • La mémoïsation transforme des algorithmes exponentiels en algorithmes polynomiaux.
  • Le paradigme diviser pour régner permet souvent d'atteindre des complexités logarithmiques.
  • L'analyse de complexité par relations de récurrence est essentielle pour évaluer l'efficacité des algorithmes.
  • La version optimisée de la multiplication montre qu'un bon algorithme peut être exponentiellement plus rapide.