• Aucun résultat trouvé

La fonction exit stoppe brutalement l’exécution du programme dans sa totalité. Le contrôle est rendu à la tâche qui avait démarré le programme.

Cette fonction doit n’être utilisé que dans les cas d’erreurs impossible à gérer.

Syntaxe C 15.32 Instruction exit exit(exprNum) ;

où :

• exit : identifiant de la fonction

• exprNum : expression numérique qui sera retournée à la tâche qui a lancé le programme ; cette dernière pourra ainsi décider de la marche à suivre en fonction de la valeur.

15.5 Exercices

Pour chacune des procédures, décrire son algorithme et donner un exemple de son appel dans un algorithme principal :

• operation attend 2 nombres entiers a et b, un caractère c, et écrit "a c b vaut r" où r est le résultat de l’opération : + pour l’addition, - pour la soustraction, * pour la multiplication et / pour la division (dans les autres cas, on affichera "opération inconnue").

• afficherEtudiant attend 1 variable de type structure étudiant (numéro : entier, nom : chaine de caractères) et écrit "l’étudiant de numéro X se nomme Y", où X et Y corres-pondent au numéro et au nom de l’étudiant.

• afficherTab1 e. attend 1 tableau de NB entiers (vecteur) et un caractère c f. et écrit les valeurs du tableau : horizontalement sur une ligne si le caractère c vaut H ou verticale-ment sur une colonne dans le cas contraire.

• AfficherTab2 attend 1 tableau d’entiers à 2 dimensions, NBLIG X NBCOL (matrice), et écrit le contenu du tableau sous forme d’une matrice (NBLIG lignes et NBCOL colonnes).

• AfficherDiagonale attend 1 tableau d’entiers à 2 dimensions NB X NB (matrice carrée) et écrit les valeurs de la diagonale de la matrice.

• Question : est-il possible de transformer la procédure operation en une fonction qui renverrait le résultat du calcul ?

– Proposer l’algorithme d’une fonction et son appel dans un algorithme principal

– Critiquer cette solution

– Quelle est la spécificité d’un sous-programmefonction au regard d’un sous-programme procédure

Huitième partie

Récursivité

16 Récursivité

La récursivité, en informatique, est une démarche de résolution de problèmes qui définit au moins 2 cas :

• un (ou plusieurs) cas de résolution terminal, sans appel récursif

• les autres cas de résolution en appliquant le même algorithme avec un jeu de données réduit qui fournit une solution intermédiaire.

Un algorithme récursif s’appuie sur une définition par récurrence d’une suite de valeurs à utiliser. Il s’appelle généralement lui-même en passant en paramètres les données permettant les calculs intermédiaires.

Une fonction récursive comporte toujours au moins une alternative : Algorithme 37 La récursivité

1: si conditionDArret alors

2: cas trivial

3: sinon

4: cas général

5: fin si

qui permet d’orienter vers au moins 2 cas :

• le cas trivial : ce cas ne doit pas effectuer d’appel récursif ; il est déterminé par la condition d’arrêt de l’appel récursif

• le cas général : ce cas effectue un appel récursif avec diminution de la valeur de son paramètre ( = traitement d’un ensemble moins grand doit converger vers le cas d’arrêt d’appel récursif)

16.0.1 Récursivité simple

Un exemple classique est celui du calcul de la factorielle d’un nombre. La factorielle peut être traitée efficacement en utilisant un algorithme itératif, mais plus élégamment en utilisant la récursivité.

La factorielle d’un nombre n peut être définie ainsi : n ∈N

f actorielle(n) =

(1 si n= 0 n∗f actorielle(n−1) sinon L’algorithme correspondant sera :

Algorithme 38 Calcul de la factorielle d’un nombre - récursivité

1: Fonction factorielle(n : entier) : entier Pre-condition: (n >= 0)

2: si n= 0 alors

3: retourner 1

4: sinon

5: retourner n * factorielle(n-1)

6: fin si

7: fin Fonction

Code source 99 – Fonction de calcul récursif de la factorielle d’un nombre 1 /∗ pre−c o n d i t i o n : (n >= 0) ∗/

2 long f a c t o r i e l l e (long n)

3 {4 i f (n == 0) {return 1;}

5 e l s e {return (n∗ f a c t o r i e l l e (n − 1 ) ) ; } 6 }

La fonction récursive met en attente chaque appel récursif jusqu’à ce que la valeur de factorielle(n - 1) ait été calculée et retournée : on a ainsi un arbre d’appel qui va devoir empiler des calculs intermédiaires dans l’attente du résultat renvoyé par l’appel de factorielle : n∗f actorielle(n−1)

Par exemple, dans le cas de la fonction factorielle, le développement du calcul de facto-rielle(3) serait le suivant :

Pour une grande valeur de n, l’arbre d’appel sera de grande taille : cela aura une incidence non négligeable sur les performances (temps et espace de données réservé pour la mémorisation des résultats intermédiaires)14.

16.0.2 Notion d’ordre dans les appels récursifs

La notion d’ordre définit le nombre d’appels réalisés dans une instruction d’appel récursif.

L’ordre est généralement 1 pour signifier que la fonction récursive est appelée une seule fois à chaque appel

Ordre

Par exemple, le calcul du nième terme de la suite de Fibonacci utilise un appel récursif d’ordre 2 :

Code source 100 – Fonction fiborec : calcul du nième terme de la suite de Fibonacci 1 /∗ pre−c o n d i t i o n : (n >= 0) ∗/

16.0.3 Quand utiliser la récursivité

Un algorithme récursif est à privilégier s’il améliorer très sensiblement la lisibilité et la com-préhension de la résolution par rapport au même algorithme itératif.

Cependant, la complexité des 2 formes (récursif et itératif) doit être étudiée afin de déterminer l’écart entre les 2. Si l’ordre de grandeur change de manière très défavorable pour l’algorithme récursif, il faut privilégier l’algorithme itératif.

De plus, l’implantation dans un langage de programmation va amener des contraintes comme la limite de capacité des nombres et les risque des débordements qui y sont liées.

16.0.4 Exercices

• Ecrire une fonction permettant le calcul de la somme des n premiers nombres entiers naturels : en utilisant un algorithme itératif, puis en utilisant un algorithme récursif et décrire l’arbre d’appels pour S(4)

14Dans un programme, un espace mémoire dynamique, la pile, permet l’empilement des résultats intermé-diaires. Quand la pile déborde, un message de type "stack overflow" (débordement de pile) est généralement affiché et l’exécution du programme s’interrompt brutalement

• Ecrire une fonction permettant le calcul de la factorielle d’un nombre naturel en utilisant un algorithme itératif, puis en utilisant un algorithme récursif et décrire l’arbre d’appels pour F(4)

• Ecrire une fonction permettant le calcul de la puissance d’un nombre a élevé à la puis-sance d’un autre nombre b en utilisant un algorithme itératif, puis en utilisant un algo-rithme récursif et décrire l’arbre d’appels pour p(2, 3)

• Ecrire une fonction permettant le calcul du plus grand commun diviseur de 2 nombres entiers positifs en utilisant un algorithme itératif, puis en utilisant un algorithme récursif et décrire l’arbre d’appels pour pgcd(12,5)

Indication : pgcd(a,b)=a si b =0, pgcd(a,b) = pgcd(b, a mod b) sinon

• Ecrire une fonction permettant le calcul de la racine carrée d’un nombre réel positif (algorithme de Newton) en utilisant un algorithme itératif puis en utilisant un algorithme récursif

Indication : la suite est définie par u0 = 1, un+1 = (un + a / un) / 2

• Ecrire une fonction permettant le calcul du nième terme de la suite de Fibonacci, en utilisant un algorithme itératif puis en utilisant un algorithme récursif

Indication : la suite est définie par u0 = 0, u1 = 1, un+1 = (un-1 + un-2) pour n >=2

Neuvième partie

Complexité

17 Complexité des algorithmes

Plusieurs solutions peuvent aboutir à la résolution d’un problème. Chaque solution va produire un algorithme différent.

Une estimation de la performance relative de chacun des algorithmes devra être calculée afin de guider le choix vers le meilleur (attention : d’autres critères sont à prendre en compte, comme la maintenabilité).

La notion de complexité d’un algorithme est une mesure de son efficacité en termes de :

• complexité temporelle : ordre de grandeur du temps nécessaire à son exécution et de son évolution en fonction des données d’entrée (lié au nombre d’opérations élémentaires réalisées : affectations, opérations arithmétiques, comparaisons, etc.)

• complexitéspatiale : ordre de grandeur de l’espace occupé par les données et de son évo-lution en fonction des données d’entrée (lié à l’espace mémoire nécessaire à l’exécution).

Cette mesure est complexe : on s’intéressera d’avantage à un ordre de grandeur permettant de comparer plusieurs algorithmes, qu’à une valeur précise de la mesure.

Il s’agit donc de déterminer une fonction représentative de la complexité en fonction de la grandeur des données fournies à l’algorithme.

Une fois l’implémentation réalisée dans un langage de programmation, la mesure pourra être validée en mesurant effectivement le temps d’exécution et l’espace mémoire utilisé

• sur une machine spécifique (avec sa propre performance généralement mesurée en flops15 )

• et un compilateur spécifique ou bien avec des paramètres de compilations spécifiques.

On détermine la fonction de complexité, f(n), en calculant le nombre d’opérations élé-mentaires réalisées en fonction d’une grandeur extérieure n, n étant une quantité de données d’entrée (valeur, liste de valeurs, grandeur) ; n est le paramètre de la complexité.