TD de MAPLE 6 Mickaël Péchaud 18 novembre 2008 Quelques compléments. 1 Algorithmique de base 1.1 Définitions 1.1.1 Un algorithme, c est quoi? Un algorithme se définit comme une suite finie d opérations, agissant sur des données et renvoyant un résultat. 1.1.2 Quelques conseils Dans le cadre d une épreuve à Polytechnique, il vous est surtout demandé de montrer si vous maitriser des concepts de base de la création d algorithmes (variables, boucles, structures conditionnelles, etc...). En particulier, ceci signifie qu il faudra éviter d utiliser les fonctions supérieures qui sont implémentées dans maple pour des questions de facilité. Il faut que vos algorithmes soient immédiatement transposables dans un autre pseudo-langage de programmation qui ne posséderait pas les même fonctionnalités. Par exemple, si vous devez trouver l élément maximal d une liste, il faut écrire vous-même votre fonction max, et non pas utiliser celle qui est prédéfinie dans maple. Idem si vous voulez trouver l intersection de 2 ensembles. Indentez vos programmes pour les rendre plus lisibles. N hésitez pas à créer des fonctions auxiliaires : si l algorithme qu on vous demande de trouver nécessite d ajouter des éléments à des listes toutes les 2 lignes, créez une fonction pour faire ça. 1.1.3 Complexité d un algorithme La complexité d un algorithme est une mesure de la vitesse à laquelle il s éxécute en fonction de la taille des données. Typiquement, si l algorithme prend comme argument un tableau, la taille des donées sera la taille du tableau. Pour mesurer la vitesse d éxécution d un algorithme, une façon de faire est de simplement compter le nombre d instructions appelées au cours de son déroulement. On appelera T d le temps d éxécution de l algorithme sur la donnée d. 1
On dit qu un algorithme s éxécute en temps linéaire par rapport à la taille de la donnée, ou encore qu il s éxécute en temps O(n) si il existe une constante k telle que pour toute données de taille inférieure ou égale à n, on ait T d < kn asymptotiquement. Pour un algorithme sur un tableau, celà revient à dire que le tableau sera parcouru au maximum un nombre p de fois, p ne dépendant pas de n. On dit qu un algorithme s éxécute en temps quadratique par rapport à la taille de la donnée, ou encore qu il s éxécute en temps O(n 2 ) si il existe une constante k telle que pour toute données de taille inférieure ou égale à n, on ait T d < kn 2 Plus généralement, un algorithme s éxécute en un temps Of(n) s il existe k tel que tel que pour toute données de taille inférieure ou égale à n, on ait T d < kf(n) 1.2 Exemples 1.2.1 Maximum d une liste On se propose d écrire un algorithme qui renvoie la valeur maximale d une liste. On peut par exemple faire ça comme suit : mon_max:=proc(t) local i, max; max:=t[1]; for i from 2 to nops(t) do if t[i]>max then max:=t[i] fi; od; max; Quelle est la complexité de cet algorithme? Soit n la taille du tableau donné en argument. Regardons combien maple effectue d instructions : Tout d abord, il initialise max : 1 opération. Ensuite une boucle qui parcourt tous les éléments du tableau est lancée. Le corps de la boucle est donc parcouru n fois. Que se passe-t-il à l intérieur de la boucle? il y a 1 test et éventuellement 1 affectation, soit au plus 2 instructions. Le temps d éxécution est donc majoré par 1 + 2n L algorithme s exécute donc en temps linéaire. Quelques remarques : on remarque que les instructions d initialisation sont souvent effectuées en temps constant par rapport à la donnée. Elle n interviendront donc pas au final dans la complexité. 2
le calcul est très approximatif : toutes les instructions de base n ont pas la même durée, maple effectue des opérations de façon implicite (par exemple incrémenter i et vérifier sa valeur), etc...ceci ne pose pas de problème car en général, nous ne sommes pas vraiment intéressés par la taille de k, nous voulons juste savoir que notre algorithme est en O(n) plutôt qu en O(n 2 ). 1.2.2 Un algorithme de tri Le fait de trier les éléments d un tableau par ordre croissant est une opération très souvent utilisée en informatique. Une façon intuitive de faire ça est d essayer de mettre l élément maximal à la fin du tableau, puis de recommencer avec le tableau constitué des n 1 premiers éléments. Pour faire remonter l élément maximal, il suffit de comparer les éléments adjacents en partant du début du tableau, et à les permuter si ils sont dans le mauvais sens. tri:=proc(t) local i, j, r, prov; r:=t; for j from 1 to nops(r)-1 do for i from 1 to nops(r)-1 do if r[i] > r[i+1] then fi; od;od; r; prov:=r[i]; r[i]:=r[i+1]; r[i+1]:=prov; Quelle est la complexité de cet algorithme? L initialisation est effectuée en temps constant. Les instructions situées à l intérieur des boucles sont effectuées en temps constant : < 1 test + 3 affectations. Combien de fois passe-t-on à l intérieur de la boucle? on voit immédiatement que l on passe (n-1)(n-1) fois. L algorithme est donc de compléxité quadratique. On peut lui ajouter une petite optimisation qui ne coute pas grand chose : en effet, lorsque l on est passé k fois dans la boucle j, les k plus grands éléments du tableau sont déjà triés. Il suffit donc que i varie de 1 à nops(t) - j. On passe deux fois moins souvent dans la boucle, mais l algorithme reste de complexité quadratique. 3
2 Récursivité La récursivité est le fait qu un algorithme s appelle lui-même. Considérons un exemple simple : la fonction factorielle. Une façon naturelle de la définir est de dire que : 0! = 1 n! = n(n-1)! Ceci est immédiatement transposable en maple : fac:=proc(n) if n=0 then RETURN(1); fi; n*fac(n-1); Dans une fonction récursive, il faut traiter le cas de base (ici n=0) sans quoi la fonction va s appeler à l infini, et maple donnera un message d erreur du type too many levels of recursion. 3 Passage par référence Depuis le début des tds, je vous ai dit qu une fonction en maple ne pouvait pas modifier ces arguments. par exemple, on ne peut pas écrire quelquechose de ce genre : inverse:=proc(a, b) local prov; prov:=a; a:=b; b:=prov; pour échanger les valeurs de a et de b : à l appel, a et b sont remplacées par leur valeur. Par exemple si b vaut 1 et a 3, on se retrouve avec 3 :=1, ce que maple refuse. En fait, en informatique, on a très souvent envie de pouvoir faire en sorte qu une fonction modifie ses paramètres. Une façon de faire ça s appelle les passages par référence. Cette technique est hors programme, mais était nécessaire à l X l an dernier pour répondre de façon précise à la première question... On peut faire ça de façon (trop) transparente en maple en utilisant par exemple le type array. array est simplement un type prédéfini de tableau en maple. Pour utiliser un array, il faut commencer par le déclarer, comme pour une matrice : 4
T:=array(1..10); ou T:=array([1, 4, 2, 7]); Pour de raisons liées à la façon dont ce type est contruit, une fonction qui prend un array en argument va pouvoir modifier son contenu. Par exemple, si on veut une procédure qui transforme le premier élément d un tableau en 1 : mettre_un:=proc(t) T[1]:=1; RETURN; S:=array([2, 2, 3]); mettre_un(s); print(s); [1, 2, 3] 4 Quelques exercices 4.1 Deux plus grands éléments Ecrire une procédure de complexité linéaire qui renvoie les 2 plus grands éléments d une liste. 4.2 Tours de hanoi : On dispose de n plateaux de tailles différentes. Les plateaux peuvent occuper 3 emplacements. Le but du jeu est de passer à la configuration où tous les plateaux sont empilés sur l emplacement i à celui où tous les plateaux sont empilés sur l emplacement j. On a les deux règles suivantes : on ne peut déplacer qu un plateau à la fois à un emplacement donné, les tableaux sont empilés par taille décroissante Ecrire un algorithme récursif qui renvoie en fonction de n la liste des déplacements à effectuer pour aller de l état initial à l état final (sous forme d une séquence de paires d emplacement par exemple ([départ, arrivée])). 4.3 Structure d arbre La stucture d arbre est quelquechose de très important en informatique. En particulier les expressions maple sont représentées sous forme d arbre. Un arbre est constitués de noeuds, reliés entre eux par une relation de paternité telle que chaque noeud n a qu un seul père. L ancêtre commun s il existe s appelle la racine, et les éléments qui n ont pas de fils sont des feuilles. Un arbre peut être définit récursivement comme étant soit une feuille, soit une liste d arbres. 5
L expression a + b c est par exemple stockée de la façon suivante par maple. + a * b c On constate que les noeuds représentent les opérateurs, et que les feuilles contiennent les valeurs. Écrire une procédure qui prend une expression maple en argument et renvoie la liste de ses feuilles. (on utilisera les commandes op et nops) Considérons un arbre dont tous les noeuds contiennent des entiers. On peut représenter un noeud comme une liste contenant l entier contenu dans le noeud, ainsi que les sous-arbres du noeud. Par exemple, l arbre suivant 1 2 4 5 6 \ 0 est représenté par : [1, [2], [4, [5], [6, [0]] ] ] Écrire une procédure qui prend en argument un arbre sous cette forme et qui renvoie l élément maximal contenu dans l arbre En écrire une autre qui renvoie la profondeur de l arbre (ie la longueur du plus long chemin de la racine à une feuille). 4.4 Diviser pour régner : le tri fusion Le concept diviser pour régner est souvent utilisé en informatique pour accélérer les algorithmes. Il consiste à séparer la donnée du problème en 2, à traiter le problème sur chacune des deux parties, et à recoller les morceaux. Intuitivement, si on a un algorithme de tri de complexité n 2, on voit que si l on sépare le tableau en 2, on peut classer les 2 sous-tableaux en un temps (n/2) 2 + (n/2) 2 = n 2 /2 Si on arrive à fusionner les 2 tableaux assez rapidement, par exemple en temps linéaire, on est passé d un complexité n 2 à une complexité n 2 /2 + n. 6
On peut évidément appliquer la même méthode aux sous-tableaux, puis aux sous-sous-tableaux, etc, etc... Ecrire une fonction qui fusionne 2 tableaux classés pour en faire un grand tableau classé en temps linéaire (on pourra travailler avec 2 indices qui parcourent les 2 tableaux progressivement). Ecrire une fonction récursive de tri s appuyant sur la méthode diviser pour régner. NB : On peut montrer que cet algorithme est de complexité nlog(n). On peut également démontrer qu il n existe pas d algorithme de tri de complexité asymptotique inférieure. 7