Scilab et algorithmique
Transcription
Scilab et algorithmique
Chapitre 1 Scilab et algorithmique I Présentation du logiciel Scilab Scilab est un logiciel libre développé par un consortium constitué : – d’organisations publiques (entre autre l’INRIA, le CNES, le CEA), – d’entreprises (Peugeot, Renault, EDF, etc.), – d’écoles et d’universités (entre autres l’école Polytechnique). On peut le télécharger gratuitement 1 . Scilab est un logiciel de calculs numériques, il ne sait donc pas gérer des variables qui n’ont pas de valeurs. Ainsi, il peut calculer cos(x) et cos(-x), si x a une valeur, mais n’a aucune idée de la signification de la formule cos(x)=cos(-x). De même, il est incapable de faire des calculs du type √ √ (1 + 2)2 = 3 + 2 2. Il peut vérifier l’égalité 3/2 = 6/4, en calculant les valeurs des deux membres et en comparant ces valeurs, mais ne simplifiera pas les fractions tout seul. Par contre, il permet de : – programmer des fonctions, i.e. des instructions exécutées sur des données et pouvant être appelées, dans l’espace de travail, voire dans d’autres fonctions. – travailler avec des données sous forme matricielle de manière efficace. Ainsi, on pourra faire l’opération A × X, si A et X sont des matrices de tailles compatibles. – contrôler le flux d’exécution i.e. exécuter certaines instructions plusieurs fois (on parle alors de boucle), ou choisir les instructions exécutées selon les valeurs d’une variable. Ce contrôle du flux se fait à l’aide de mots clés, comme if, end, for et while. – travailler de manière très rapide avec les fonctions mathématiques. La plupart des fonctions mathématiques sont déjà implémentées, en particulier les algorithmes complexes pour le calcul des probabilités et le calcul matriciel. – afficher des résultats sous forme graphique. Scilab au concours de BCPST Dans la quasi totalité des sujets d’écrit de concours, une question Scilab est présente, notée entre 2 et 4 points. Il faut donc savoir écrire de petites fonctions Scilab pour l’écrit. Une erreur de syntaxe sans gravité n’est pas sanctionnée lourdement. Par contre, les erreurs d’algorithmique sont davantage sanctionnées. 1. Par exemple, sur le site officiel : http://www.scilab.org/. 1 Une épreuve orale d’option est aussi prévue, sous forme d’un projet et d’exercices à l’oral. La moyenne de l’épreuve tourne autour de 13 et seuls les points au-dessus de 10 sont pris en compte. Dans les rapports de jury, il est systématiquement rappelé qu’un effort en informatique est payant et fait gagner des points, à l’écrit comme à l’oral. Enfin, rappelons que, dans la plupart des écoles que l’on peut intégrer avec une BCPST, un cours d’informatique est prévu en première année. Il s’agit très souvent d’un cours sur un logiciel de calculs numériques (Scilab ou Matlab 2 ), ainsi le temps consacré à l’informatique en BCPST n’est pas du « temps perdu », même si c’est une matière nouvelle par rapport à la classe de Terminale et qui demande donc un certain investissement au début. II Prise en main du logiciel : les trois interfaces de travail Scilab propose trois environnements de travail : la ligne de commande, les scripts et les fonctions. La ligne de commande C’est l’interface de travail par défaut : on tape une commande et le calcul est fait. Le résultat est affiché si la ligne ne finit pas par ;, sinon il est effectué sans affichage. On peut retrouver les commandes déjà tapées en utilisant ↑. Application 1 Tapez les commandes dans l’espace de travail, tout ce qui suit le symbole // sont des commentaires, il est donc inutile de les recopier. 2+2 //exemple de calcul simple 2+2; // le même avec un ; (1-%i)* 2 // exemple de calcul dans C : %i est le i complexe 3*2 // exemple de multiplication sqrt(2) // appel d’une fonction mathématique %pi // constante pi i*2 // ne pas oublier les % log 2 // ni les (). Lorsque le flux d’exécution est suspendu (i.e. lorsqu’on se trouve dans une boucle for ou un if), alors rien ne se passe jusqu’à ce que l’on entre le mot clé end. Pour indiquer que les exécutions sont suspendues, les invites de commandes ––> sont rapprochées. Application 2 Taper les commandes : for i=1:10 disp(i) end Remarquez la manière dont les invites de commandes ––> sont rapprochées. Ce mode est utile pour des tests rapides, mais les commandes tapées ne peuvent être mémorisées 3 . D’une manière générale, il est déconseillé de taper des commandes permettant de contrôler le flux 2. Équivalent propriétaire du logiciel Scilab. Le générateur de nombres aléatoires est moins puissant, mais l’interface est plus agréable. 3. L’historique peut être retrouvé par la commande gethistory. d’instructions dans la ligne de commande, i.e. dès qu’on utilise if, for ou while, il faut utiliser un script ou une fonction. Pour interrompre le flux d’exécution en cas d’urgence, il faut utiliser le raccourci clavier CTRL+C. L’invite de commande devient -1->, on peut alors annuler le tout en utilisant la commande abort. Application 3 Taper les commandes : i=1; while (i<10) disp(i) end Pour sortir de l’exécution, utiliser le raccourci clavier CTRL+C, puis la commande abort. Les scripts Un script est un fichier texte qui contient une suite de commandes. Lorsque le fichier est exécuté, les commandes sont exécutées une par une, comme si elles étaient tapées dans la ligne de commande. Si une erreur est rencontrée, l’exécution s’arrête. Les scripts permettent donc de sauvegarder la suite d’instructions effectuées pour pouvoir la refaire. Pour créer un script, il faut donc utiliser un éditeur de texte 4 , dans lequel on crée un fichier texte qu’on enregistre, de préférence avec l’extension .sce qui signifie Scilab executable. Puis, pour exécuter le fichier, il faut utiliser la commande exec(’fichier.sce’), en ayant pris soin de donner à Scilab le chemin du répertoire contenant le fichier 5 . Bien entendu, il faut sauvegarder le fichier avant de l’exécuter. Application 4 Ouvrir l’éditeur, enregistrez dans le fichier presentation.sce la suite d’instructions : clear; //efface toutes les variables clc; //efface l’écran nom=input("Quel est ton nom?","string"); disp("bonjour "+nom); Exécuter ce script. La solution à un exercice de Scilab sera donc toujours un script permettant de réaliser l’expérience demandée. Pour prendre de bonnes habitudes : – un script commence par la commande clear; qui permet d’effacer toutes les variables, afin de bien vérifier que d’éventuelles commandes précédentes n’interviennent pas dans le script. – Ensuite on charge les différentes fonctions, – puis la description des constantes, – enfin l’appel aux fonctions. Notons, de plus, que rien n’empêche de faire appel à un script dans un script, en utilisant la commande exec. 4. On peut utiliser celui intégré à Scilab, mais il est peu puissant. Le mieux est donc d’utiliser un programme externe, par exemple Geany. Voir sur le site web comment configurer Geany pour Scilab. 5. La commande pwd indique le répertoire de travail. On le change avec la commande cd. Il est aussi possible d’utiliser le menu. Comme tous les langages de programmation, Scilab permet l’utilisation de commentaires, tout ce qui suit // étant ignoré. Cette fonctionnalité doit être utilisée pour expliquer ce que fait le code. Dans les épreuves d’écrit, il est conseillé aussi d’ajouter des commentaires pour expliquer sa démarche. Un code sans commentaires n’a aucune valeur. De même, les espaces entre les mots ne sont pas pris en compte lors de l’exécution, ce qui est particulièrement intéressant pour les tabulations qui permettent de structurer visuellement le code. Les fonctions Une fonction est une suite d’instructions exécutées sur des entrées permettant le calcul de sorties. Cette suite d’instructions est enregistrée dans un fichier texte. Les entrées sont données par l’utilisateur et les instructions n’ont donc un sens pour Scilab qu’à l’appel de la fonction sur certaines entrées. L’avantage d’une fonction est qu’elle peut être appelée sur n’importe quel type d’entrée, exactement comme une fonction mathématique. Par exemple, si on considère la fonction function [out]=f(x,y), on pourra l’appeler sous la forme f(2,i), mais aussi sous la forme 6 f(x,x), f(2x,y), etc. On pourra aussi utiliser l’instruction f(f(x,y),z). Il ne faut donc pas donner de valeurs aux variables d’entrées dans la fonction. Une fonction du type f : x 7→ x2 + 3x + 2, qui serait implémentée sous la forme : function out=f(x) x=2; out=x^2+3*x+2; endfunction ne fera que renvoyer la valeur 12, ce qui n’est absolument pas la fonction demandée. De même, une fonction doit systématiquement donner une valeur aux sorties, sinon elle ne calcule rien. La fonction f doit donc contenir une instruction du type out=, où out est le nom de la variable de sortie 7 . Ces deux erreurs (donner une valeur aux entrées dans la fonction et ne pas donner de valeur aux sorties) sont considérées comme des erreurs d’algorithmique. La syntaxe d’une fonction est très particulière. Il faut indiquer à Scilab que les variables en entrée n’ont pas de valeurs, et que les instructions n’auront de sens que lors de l’appel de la fonction. Une fonction doit donc toujours commencer par une ligne function [y1,y_2, ... , yn]= nom_fonction(x1,x2 ... , xm) où les xi sont les entrées, tandis que les yi sont les sorties, et finir par la commandeendfunction. La fonction est ensuite ajoutée à Scilab via la commande exec(’fichier.sci’), l’extension .sci (« Scilab Input ») étant conseillée pour les fichiers de fonctions. Il est aussi conseillé d’utiliser un fichier par fonction, le fichier portant le nom de la fonction. Si Scilab détecte une erreur de syntaxe (si, par exemple, il manque un end), il émet une erreur en indiquant la ligne correspondante. Si la fonction est correctement chargée, une nouvelle variable est alors créée dans Scilab, portant le nom de la fonction. Une fonction peut ainsi être utilisée comme entrée d’une autre fonction. La fonction peut alors être exécutée, en tapant la commande correspondante, les entrées étant indiquées dans une parenthèse. Par exemple, on doit entrer dans l’éditeur les lignes : 6. Si x est une variable qui a déjà une valeur évidemment. 7. Ceci étant valable bien sûr pour toutes les sorties si il y en a plusieurs. function out=f(x,y) out=cos(x)*y; endfunction Ces lignes doivent être enregistrées dans un fichier f.sci. Ensuite, on doit charger la fonction, i.e. exécuter la commande exec(’f.sci’). On peut ensuite l’appeler avec, par exemple : f(%pi/4,%i);//renvoie 0.7071068i x=2;y=4; z=f(x+y,y);//stocke - 1.117662 (soit 4sin(6)) dans z f(f(1,1),0.3) // exemple de composition. Si il y a plusieurs sorties, seule la dernière est affichée, le reste peut être récupéré en stockant les valeurs de sorties. Par exemple : function [u,v]=f(x,y) u=x+y; v=x-y; endfunction La commande f(3,4) ne renverra que la valeur 7. Pour récupérer la valeur -1, il faut taper : [x,y]=f(3,4); la variable x contiendra alors 7, et y contiendra -1. Notons enfin que les instructions de la fonction ne sont effectuées que lorsque la fonction est appelée, ce qui peut poser des problèmes si la fonction est appelée sur des entrées, en dehors de son domaine de définition. Par exemple, une fonction f : (x, y) 7→ . . . définie si (x, y) ∈ R2 , et contenant l’instruction if x < y, ne posera aucun problème lors du chargement de la fonction. Par contre, un appel de la fonction du type f (i, 4) provoquera un message d’erreur. C’est pourquoi, il est conseillé d’écrire des commentaires, au début d’une fonction, un petit descriptif des entrées et des sorties. De même que l’on doit préciser le domaine de définition d’une fonction en mathématiques. Enfin, pour modifier une fonction, il faut modifier le fichier, puis le sauvegarder et le recharger (de nouveau avec la commande exec). Un avertissement est alors affiché pour signaler que la fonction est redéfinie : Warning :redefining function: nomfonction. Application 5 Ouvrir l’éditeur et sauvegardez dans le fichier demandeAge.sci les lignes : function age=demandeAge(prenom) //entrée: prenom (chaîne de caractère) //sortie: age (réel)= âge donné //Cette fonction demande l’âge de la personne dont le prénom est en entrée disp("Bonjour "+ prenom); age=input(prenom+ ", quel est ton âge?"); endfunction Pour appeler cette fonction : – enregistrez votre fichier, – chargez la fonction avec la commande exec(’’demandeAge.sci’’); – appelez-la avec une commande du type demandeAge(’’toto’’); Que se passe-t-il si on appelle la fonction avec demandeAge(5); ? Modifier la fonction pour qu’elle affiche merci à la fin. III Algorithmique : Écrire un programme Définition 1. Un algorithme est une suite finie d’instructions déterministes permettant d’effectuer un calcul, d’obtenir la valeur d’une fonction ou, d’une manière générale, de résoudre un problème. On s’intéressera en mathématiques à des algorithmes du type : √ – a étant donné, calculer une approximation de a, en donnant une marge d’erreur sur cette approximation. – A et b étant donnés, résoudre le système Ax = b. – Renvoyer une approximation de π par la méthode de Monte-Carlo en utilisant 1000 lancers. π – Calculer une valeur numérique de sin( 21 ). – Trouver la trajectoire d’un pendule soumis à la gravité. Mais l’informatique peut aussi être utilisée pour : – modéliser sous forme graphique la propagation d’un feu dans une forêt ou les déplacements de fourmis à la recherche de nourriture, – étant donné une position sur un jeux d’échecs, donner la liste des coups possibles. Plusieurs remarques s’imposent : – par instruction, on entend un « ordre donné à l’ordinateur », c’est-à-dire faire un calcul, appeler une fonction, affecter une variable, effectuer un test, etc, mais en aucun cas, on ne peut prendre le point qui réalise le maximum de la fonction f sur l’intervalle [0, 1], si on ne dispose pas de moyens effectifs de calculer ce maximum. C’est la différence entre l’informatique et les mathématiques : en mathématiques on peut montrer l’existence de certains objets, en informatique, on a besoin de pouvoir les calculer. – utiliser des instructions déterministes n’empêche par l’utilisation d’aléatoires. En fait implicitement un générateur de nombres aléatoires est ajouté aux entrées. Par contre, étant donné des entrées identiques (dont le générateur aléatoire), les sorties seront identiques, encore une fois comme pour une fonction en mathématiques. – le problème de la finitude est complexe : il faut pouvoir prouver qu’un algorithme s’arrête et ne boucle pas indéfiniment, en particulier dans le cas d’algorithmes récursifs, qui s’appellent eux-mêmes. – s’ajoute à ces notions une définition de l’efficacité. Un algorithme doit faire le moins de calculs possibles pour calculer la sortie, de manière à diminuer le temps de calcul. De plus, un algorithme doit, si possible, utiliser le minimum d’espace mémoire. IV Utilisation de variables La première notion informatique liée à celle d’algorithme est la notion de variable : une variable est un bloc de mémoire qui est nommé. Ainsi, l’instruction a=2; alloue un bloc de mémoire dans la RAM et y insère la valeur 2. On parle d’affectation de la variable a. Le caractère a fait alors référence à ce bloc de mémoire. Lorsqu’on tape l’instruction b=a+2, le bloc de mémoire est lu, l’opération est effectuée et le résultat est stocké dans la variable b. Ainsi, le = en informatique n’a pas le même sens que le = mathématique, en particulier l’instruction b=a n’est pas identique à l’instruction a=b : – b=a lit le contenu de la variable a, et stocke ce contenu dans la variable b, a n’est donc pas modifiée, – a=b lit le contenu de la variable b, et stocke ce contenu dans la variable a, b n’est donc pas modifiée. C’est donc exactement le contraire. Cette remarque permet d’utiliser des instructions comme a=a+2, qui ajoute 2 au contenu de la variable a. Ainsi, pour itérer une fonction à partir d’un point a, i.e. pour calculer les termes d’une suite (un ) définie par u0 = a et un+1 = f (un ), si on n’a besoin que du dernier terme de la suite, on pourra utiliser l’instruction x=a, pour initialiser une variable x à la valeur de a, puis répéter l’instruction x=f(x). Si on utilise une variable non affectée, on obtient alors un message d’erreur explicite undefined variable : x. Utiliser une variable sans lui avoir donner de valeur est considéré comme une faute d’algorithmique. Bien entendu, dans une fonction, les variables en entrées sont supposées être affectées, ou plus exactement sont affectées lors de l’appel de la fonction. Application 1 Tapez la suite de commandes, et observez le résultat : a=1; //affectation de variables le ; évite l’affichage a b=a+3; b b=a+3 // influence du ; 2*b (2b-1) /2 //ne pas oublier les * b*2-1/2 // corrigez en utilisant la flèche (b*2-1)/2 // attention aux parenthèses B // B est différent de b Application 2 Effacez l’espace de travail avec la commande clear; puis tapez la commande : a=a+3; Que se passe-t-il ? Application 3 Si l’on exécute la suite de commande : a=2; a=a*a; a=a*a; a=a*a; Que contient la variable a ? Application 4 Si l’on effectue la suite de commande : a=5; b=3; c=a; a=b; b=c; Quelles valeurs contiennent les variables a et b ? Cette technique permet de transférer la valeur d’une variable dans une autre. Application 5 Calculez rapidement le 5-ième terme de la suite définie par : u0 = 2, et un+1 = un sin(un ). Proposez une méthode avec une fonction et une sans. Les types de données que peut contenir une variable en Scilab sont principalement : – un réel, – un complexe, en utilisant %i, – une chaîne de caractères, – une matrice ou un vecteur (considéré comme une matrice avec une unique ligne/colonne), – une variable booléenne %t (vraie=true) ou %f (fausse=false), – une fonction. La commande who_user permet d’afficher le nom des variables affectées 8 , et la commande clear permet d’effacer l’espace de travail, i.e. toutes les variables affectées. Pour connaître la valeur d’une variable, il suffit de taper son nom sans ajouter le ;, la valeur est alors affichée. Une commande graphique permet d’éditer les variables, c’est la commande browsevar();. Portée des variables La manière dont les variables peuvent être utilisées par une fonction dépend de la portée d’une variable, i.e. du domaine dans lequel cette variable a une valeur : – une variable affectée dans l’espace de travail est globale, c’est-à-dire qu’elle est connue dans toutes les fonctions. Ainsi, si dans l’espace de travail on a défini une variable f , n’importe quelle fonction peut utiliser cette variable. Ceci est particulièrement utilisé si f est une fonction. On peut considérer que l’ensemble de l’espace de travail est implicitement ajouté en entrée d’une fonction. – de telles variables peuvent être modifiées dans une fonction, mais c’est alors leur valeur locale qui est modifiée par la fonction, mais pas dans l’espace de travail. Par exemple, si dans l’espace de travail on définit n=3, une fonction pourra utiliser le contenu de la variable n et faire des opérations du type n=n+1, mais une fois sortie de la fonction, la valeur de n sera toujours 3. Cela est vrai aussi, et particulièrement utilisé pour les entrées d’une fonction : si une fonction y=f(x) modifie son entrée en utilisant des instructions du type x=x-1, l’appel de la fonction avec une instruction du type f(x) ne modifie pas la valeur de x dans l’espace de travail. – pour éviter toute erreur, il est ainsi conseillé d’utiliser uniquement des variables locales, i.e. une fonction doit pouvoir fonctionner uniquement avec ses entrées. C’est de toute manière sous cette forme que sont demandées les fonctions lors des épreuves d’écrit. – une variable utilisée par une fonction est locale. Ainsi, si une fonction définit et utilise une variable n, n n’aura pas de valeur en dehors de la fonction, ce qui permet de ne pas « ajouter » de nouvelle variable. En particulier, appeler une fonction définie par y=f(x) avec l’instruction f (2) ne définit pas de variable y. On peut ainsi appeler la fonction sur une variable nommée z et stocker le résultat dans une variable nommée x, etc. Peu importe le nom de la variable dans la fonction, cela n’intervient pas sur les variables de l’espace de travail. 8. D’autres variables « système » sont aussi affichées, il suffit de ne pas en tenir compte. Ainsi, on peut comparer une fonction Scilab à une boîte noire, calculant une sortie donnée à partir d’entrées et des variables contenues dans l’espace de travail, mais ne modifiant pas l’espace de travail. Application 6 Écrire une fonction du type : fonction y=f(x) x=x+1; y=x^2; endfunction La lancer avec la suite de commandes : exec("f.sci"); a=3; f(a) Quelle est la valeur de la variable a, celle de la variable x et celle de la variable y ? Si on tape ensuite dans la ligne de commande : x=5; f(x^2) Quelle est la valeur affichée ? Quelle est la valeur de la variable x ? Erreurs de syntaxe classiques avec les variables : – Scilab fait la différence entre majuscule et minuscule. – il faut utiliser le symbole * pour une multiplication, 2b renvoie une erreur. Pour éviter cette confusion, on ne peut pas utiliser une variable commençant par un chiffre. – les lettres grecques ne sont pas acceptées, ainsi que les lettres accentuées dans les noms de variables. On utilisera donc des noms de variables du type phi. – les instructions d’affection des variables doivent être de la forme variable=expression. Une expression du type 3=a est une erreur d’algorithmique, ainsi que a+b=c+d, qui n’a aucun sens en informatique. – dans une instruction de la forme variable=expression, c’est d’abord expression qui est évaluée et ensuite le résultat est stocké dans variable. Ainsi, l’instruction b=a+b; calcule a+b et stocke le résultat dans b. – pas d’accents ni d’espace dans les noms de variables. V Contrôle du flux d’exécution Le contrôle du flux d’exécution permet d’exécuter certaines instructions plusieurs fois, ou selon la valeur de certaines variables. V.1 Variables booléennes et instructions conditionnelles if Variable booléenne Une variable booléenne est l’équivalent informatique d’une proposition mathématique. C’est donc une variable qui ne peut prendre que deux valeurs : true (T) ou false (F). Pour la définir, on utilise un opérateur de comparaison , c’est-à-dire >,<,==,>=,<=,˜=. Il existe deux possibilités pour 6= : ~= et <>. Par exemple, si on a défini deux variables x et y, l’instruction x==y renvoie la valeur true si les variables ont la même valeur, false sinon. On utilise == pour bien signifier qu’il ne s’agit pas d’une affectation. L’instruction x=y (qui met la valeur de y dans la variable x) est très différente de x==y qui compare les deux valeurs 9 . Attention à ne pas confondre ces deux instructions : cela peut être considéré comme une erreur d’algorithmique. On retiendra donc qu’il faut toujours mettre == dans un test. Les variables booléennes peuvent être combinées à l’aide des opérateurs ~, & et | , équivalents en mathématiques à non, et et ou respectivement. Remarque: Mathématiquement, la proposition « p et q » est fausse dès que p est fausse. Du coup, lorsque Scilab rencontre une variable booléenne du type « p et q », il commence à regarder la valeur de p. Si celle-ci est fausse, alors, il n’a pas besoin de regarder la valeur de q. D’une part, cela évite des tests, et donc du temps de calcul inutile, mais l’application la plus importante est surtout que si p est fausse, la variable q peut-être indéfinie. L’exemple typique est celui d’un tableau : si on doit lire l’élement i dans un tableau tab de taille n, il faut s’assurer que i ∈ [[1, n]]. On rencontrera, par exemple le test : if (i<=n) & (i>=1) & (tab(i)==0) qui teste d’abords si i ∈ [[1, n]], avant de comparer la valeur de la case i de tab à 0. Si la valeur de i n’est pas dans l’intervalle, alors tab(i) n’est pas lu. Au contraire, si on avait écrit : if (tab(i)==0) & (i<=n) & (i>=1) alors, si la valeur de i n’est pas dans l’intervalle, Scilab renverra une erreur puisqu’on essaie de lire un élément qui n’existe pas. On peut donc dire qu’en informatique « p et q » n’est pas rigoureusement identique à « q et p ». Instructions conditionnelles : if Les variables booléennes sont surtout utilisées pour des instructions conditionnelles, c’est-à-dire que l’on peut donner des instructions différentes à l’ordinateur selon la valeur d’une variable booléenne. On contrôle le flux d’exécution selon la valeur des variables booléennes. De même qu’en mathématiques on peut définir la fonction x 7→ on peut définir la fonction Scilab : sin(x) x 0 si x 6= 0 sinon , function y=f(x) if (x==0) y=0; else 9. Notons d’ailleurs que x==y est identique à y==x, alors qu’on a vu que x=y est le contraire de y=x. y=sin(x)/x; end endfunction La syntaxe générale d’une instruction if est donc : if (test) then //bloc d’instructions si test vrai else //bloc d’instructions si test faux end Le then est optionnel, il peut être remplacé par un retour à la ligne. La partie else est aussi optionnelle : il peut très bien n’y avoir aucune instruction si la condition est fausse. Application 1 Écrire une fonction out=potentielHydrogene(pH) qui prend en entrée un nombre pH, et qui renvoie 1 si le ph est basique, 0 si il est neutre, et -1 si il est acide. Modifiez la fonction pour qu’elle sorte une chaîne de caractères : basique, neutre ou acide (selon les cas). On peut enchaîner les instructions conditionnelles en utilisant elseif, qui permet d’éviter de devoir mettre plusieurs end. La syntaxe de base est : if (test) then //bloc d’instructions si test1 vrai elseif(test2) then //bloc d’instructions si test1 faux et test2 vrai else //bloc d’instructions si test1 faux et test2 faux end Par exemple, l’implémentation de la fonction : signe : peut s’écrire : function y=signe(x) if (x>0) then y=1; elseif(x<0) then y=-1 else y=0 end endfunction x 7→ 1 si x > 0 −1 si x < 0 0 sinon Comme on le voit, il est important de tabuler correctement le programme pour voir clairement quelles sont les instructions répétées. On peut aussi imbriquer les structures conditionnelles avec la syntaxe : if (test1) //début des instructions si test1 vrai ... if(test2) //instructions si test1 vrai et test2 vrai end//fin du if(test2) //suite et fin des instructions si test1 vrai else //début des instructions si test1 faux ... if(test3) //instructions si test1 faux et test3 vrai else //instructions si test1 faux et test3 vrai end//fin du if(test3) //suite et fin des instructions si test1 faux end Pour éviter toute confusion, il vaut mieux écrire, en commentaire, à quel if correspond chaque end. V.2 Instructions répétées : boucles Une boucle et un bloc d’instructions qui est répété plusieurs fois. Il existe deux manières de faire des boucles : les boucles for et les boucles while. Boucle for Si on connaît d’avance combien de fois il faut répéter le bloc d’instructions, on utilise alors le mot clé for, qui permet d’indexer le nombre de répétitions sur une variable. La syntaxe est : for var=début:pas:fin //bloc d’instructions ... end Cela permet d’initialiser une variable (ici var) à la valeur début. Ensuite, le bloc d’instructions entre le for et le end est exécuté. Dans ce bloc, on peut utiliser (ou pas) la valeur de la variable var. Ensuite, la variable var est incrémentée de la valeur pas. Si la variable var est inférieure ou égale à la valeur de fin, le bloc d’instructions est de nouveau exécuté et la variable de nouveau incrémentée. Lorsque la variable var dépasse la valeur de fin, l’instruction continue après le end (on dit que l’instruction sort de la boucle). Le pas peut-être omis et vaut alors 1 par défaut. Considérons, par exemple, une fonction qui calcule la somme des n premiers entiers. Elle peut être implémentée avec une boucle for : function y=somme(n) // entrée: n entier, // sortie: y somme des n premiers entiers y=0; for i=1:n y=y+i; end endfunction Application 2 À l’aide d’une boucle for, calculer 7! Application 3 À l’aide d’une boucle for, calculer la somme des carrés des vingt premiers nombres entiers. Quelques remarques : Il faut initialiser la variable y à 0 pour pouvoir utiliser l’instruction y=y+i qui permet d’ajouter i à y. Pour un calcul de somme, il est judicieux d’initialiser y à 0. Pour un calcul de produit, on préfèrera évidemment, l’initialiser à 1. Si y est un vecteur et qu’on ajoute à chaque itération une composante, alors on peut l’initialiser à la matrice vide : y=[]. Enfin, si y est une chaîne de caractères, on pourra l’initialiser à la chaîne vide via y="";. Le nombre de répétitions dans une boucle for peut être déterminé à l’avance : c’est le nombre de valeurs possibles pour la variable var. Rien n’empêche de faire des boucles du type : for k=10:-1:1, i.e. avec un pas négatif. Le programme précédent peut être écrit sous la forme : function y=somme(n) // entrée: n entier, // sortie: y somme des n premiers entiers y=0; for i=n:-1:1 y=y+i; end endfunction Une boucle telle que fin>debut est ignorée : l’exécution ne rentre jamais dans la boucle. Enfin, il n’est pas nécessaire que la variable var passe exactement par fin : par exemple, i=1:2:10 permettra d’effectuer la boucle 5 fois (pour i=1,3,5,7 et 9). Boucle while Si la boucle ne se finit que lorsqu’une condition est réalisée, on utilise alors une boucle while qui permet de répéter un bloc d’instructions jusqu’à ce qu’une condition appelée test d’arrêt soit atteinte. La syntaxe est : while (test) //bloc d’instructions répété jusqu’à ce que test soit faux ... end L’instruction teste la valeur du booléen test, qu’on appelle test d’arrêt. Si cette valeur est fausse, l’exécution continue jusqu’au end. Ensuite, le test d’arrêt est de nouveau évalué et le bloc d’instructions est répété. On ne sort de la boucle que si le test est faux. On ne peut donc pas déterminer le nombre de passages dans la boucle à l’avance. Le problème de la finitude se pose donc clairement : il faut prouver que l’on sort de la boucle. Notons que si le test d’arrêt est faux dès le départ, on ne rentre jamais dans la boucle. On peut aussi écrire la somme des n premiers entiers avec une boucle while : function y=somme(n) // entrée: n entier, // sortie: y somme des n premiers entiers y=0; while(n>0) y=y+n; n=n-1; end endfunction Quelques remarques : Ici, on utilise directement la variable d’entrée n comme la variable de test qui va donc être modifiée par la fonction. Mais c’est la copie locale de la variable qui est modifiée. Ainsi, si on tape les commandes x=5; somme(x); la variable x vaudra toujours 5. On pourrait aussi écrire function y=somme(n) // entrée: n entier, // sortie: y somme des n premiers entiers y=0; i=1; while(i<=n) y=y+i; i=i+1; end endfunction Cela impose d’ajouter une variable i. Enfin, il faut faire attention à l’ordre des opérations et au test. Par exemple, dans cette dernière fonction, si on remplace while(i<=n) par while(i<n), on fait une erreur d’algorithmique, puisqu’il manquera une exécution de la boucle : on n’ajoutera pas la valeur n. Autre erreur, si on renverse les deux lignes du bloc while, en écrivant : function y=somme(n) // entrée: n entier, // sortie: y somme des n premiers entiers y=0; i=1; while(i<=n) i=i+1; y=y+i; end endfunction On fait de même une erreur d’algorithmique, puisqu’on ajoutera les valeurs 2, 3 . . . n + 1, au lieu de 1, 2, . . . n. Pour éviter les erreurs, le plus simple est de tester au brouillon l’algorithme sur de faibles valeurs, en écrivant toutes les instructions envoyées à l’exécution. Dans l’exemple ci-dessus, en testant pour n=3, on obtient les instructions : y=0; i=1; //rentrée dans la boucle (i=1) i=i+1;// i vaut 2 y=y+2;//y vaut 2 //deuxième passage par while, (i=2) i=i+1;//i vaut 3 y=y+3;//y vaut 5 //troisième passage par whle, (i=3) i=i+1;//i vaut 4 y=y+4; y vaut 9. //fin du while car (4>3) Application 4 Algorithme de Syracuse On choisit un entier quelconque. S’il est pair, on le divise par 2, sinon on le multiplie par 3 et on ajoute 1. Puis on répète ce processus jusqu’à arriver à 1. Cet algorithme se termine toujours en un nombre fini d’itérations. Écrire une fonction qui prend en entrée un entier et : – affiche à l’écran la suite des itérés, – renvoie le nombre d’itérations qui ont été nécessaires pour revenir à 1. Quand utiliser while ? Les boucles while sont donc plus facilement sources d’erreurs, mais indispensables dès qu’on ne sait pas à l’avance combien d’itérations vont être nécessaires. C’est le cas, par exemple, si on cherche le point fixe d’une fonction. En itérant une suite un+1 = f (un ), on exécute alors la suite d’instructions : x=a; while(f(x)~=x) x=f(x) end La boucle continue jusqu’à ce qu’un point fixe soit atteint. Bien entendu, l’algorithme n’est valide que parce qu’il se finit, i.e. parce qu’on a prouvé qu’on converge. Autre exemple : supposons qu’on souhaite poser la question Voulez vous continuer ? jusqu’à obtenir la réponse o ou n. On utilise alors une boucle while, selon cette syntaxe : rep="";//on pourrait mettre n’importe qu’elle valeur différente de o ou n while ( (rep<>"o")&(rep<>"n")) rep=input("Voulez-vous continuer?","string"); end Ce bloc de commande peut lui-même être inclus dans une boucle while du type : while (rep=="o"), en ayant pris soin d’initialiser la variable rep à "o" de manière à rentrer au moins une fois dans la boucle. VI Algorithmique élémentaire Le problème de l’échange de deux variables Soient deux variables a et b. Pour échanger les valeurs contenues dans ces variables, il faut passer par une variable auxiliaire généralement nommée tmp ou aux pour bien montrer son caractère temporaire. Il faut ainsi utiliser les instructions tmp=a; a=b; b=a; Cette remarque s’étend aux calculs d’affectations les unes à la suite des autres. Par exemple, si on veut calculer les termes des suites (an ) (bn ) définies par : ∀n ∈ N, ( an+1 = bn+1 = √ an bn an +bn 2 et qu’on ne veut garder que la dernière valeur. On ne peut pas utiliser les expressions : a=sqrt(a*b);b=(a+b)/2; car, dans ce cas, la deuxième instruction utilise, non pas la valeur de a originale, mais celle modifiée par la première instruction. Il faut donc encore passer par une variable temporaire, par exemple : tmp=sqrt(a*b);b=(a+b)/2;a=tmp;. Attention à ce type d’erreur d’algorithmique, qui ne pose aucun problème de syntaxe, mais ne donnera jamais le bon résultat. Conserver ou pas des variables Supposons qu’on considère la suite de Fibonnaci un définie par u1 = 1 et u2 = 1 un+2 = un+1 + un . Pour écrire une fonction qui renvoie la valeur de un en fonction de n, on doit a priori sauvegarder toutes les valeurs des (uk )k<n dans la fonction, en utilisant un vecteur de taille n, contenant [u1 , u2 , u3 . . . , un ], et ensuite ne renvoyer que la dernière composante du vecteur 10 . Cela s’écrit : function out=fibbo(n) // n entier naturel // out = valeur du n-ième terme de la suite de fibbonacci u=[1,1]; //initialisation du vecteur u, par les deux premiers termes for i=3:n //on ajoute une composante au vecteur u somme des deux composantes précédentes: u(i)=u(i-1)+u(i-2); end 10. Voir le chapitre suivant pour la manipulation de vecteurs/matrices en Scilab. Ici, on se sert uniquement du fait que u(i) est la i-ème composante du vecteur i. out=u(n); //rem: si n<3, on ne rentre pas dans la boucle, le résultat est donc valable endfunction Le défaut de cette méthode est qu’il y a une perte de mémoire. Pour calculer u1000 , on n’a pas besoin des 999 termes précédents, mais uniquement de 2 : u999 et u998 . On peut donc raisonner différemment. On utilise deux variables : un pour la valeur de un et unm1 11 pour la valeur de un−1 . À chaque itération, i.e. pour passer de n à n + 1, on doit mettre à jour la variable un comme la somme de la valeur de un et de un−1 . Ainsi, un contient bien la valeur de un+1 . On écrit donc l’instruction : un=un+unm1;. Il faut aussi modifier la valeur de la variable unm1 pour qu’elle contienne, non plus un−1 , mais la valeur de un , qui était contenue dans la variable un avant modification et qu’on aura donc pris la peine de sauvegarder dans une variable temporaire. Le programme devient alors : function out=fibbo(n) // n entier naturel // out = valeur du n-ième terme de la suite de fibbonacci un=1;//les deux valeurs sont initialisées à 1 unm1=1; for i=3:n //on sauvegarde la valeur de un: tmp=un; //la valeur courante est mise à jour: c’est la somme des deux valeurs précédentes un=un+unm1; //la valeur de unm1 est mise à jour: c’est la valeur de un sauvegardée unm1=tmp; end out=un; //rem: si n<3, on ne rentre pas dans la boucle, le résultat est donc valable endfunction Ce dernier algorithme utilise beaucoup moins de variables. Cette idée se comprend aussi facilement dans le cas du calcul du terme un dans une suite définie par un+1 = f (un ), en fonction du premier terme u0, de la fonction f et de n. On pourrait écrire ce programme sous la forme : function y=suite(n,f,u0) // n entier naturel // f fonction telle que la suite récursive est bien définie en partant de u0 // u0 premier terme // y = valeur du n-ième terme de la suite définie par u0 et u(n+1)=f(u(n)) u=[u0];//initialisation de u à un vecteur ne contenant que le terme u0 for i=1:n u(i)=[u,f(u(i-1)];//ajout d’une composante au vecteur u 11. Abréviation de « u n moins 1 ». end out=u(n+1); endfunction Cette fonction construirait donc un long vecteur de taille n+1, contenant [u0 , u2 , . . . , un+1 ]. À chaque itération, une composante est ajoutée 12 au vecteur u, cette nouvelle composante étant l’image de la précédente par la fonction f. On pourrait aussi écrire cette fonction sous la forme plus condensée : function y=suite(n,f,u0) y=u0;//initialisation de y for i=1:n y=f(y);//on remplace y par son image par f end endfunction Quelques remarques : La fonction f est ici une entrée de la fonction suite. on utilise ici le fait qu’on peut mettre une fonction en entrée d’une autre fonction. Si on demande, dans une copie, d’écrire une fonction qui ne dépend que de n et de u0, et qui permet de calculer le n-ième terme d’une suite définie par u0 et par exemple : un+1 = sin(un ) + cos(un ), on a tout intérêt à définir la fonction f dans la fonction suite 13 . Cela donnera : function y=suite(n,u0) //définition de la fonction f: deff("y=f(x)","y=cos(x)+sin(x)"); y=u0;//initialisation for i=1:n y=f(y);//on remplace y par son image par f end endfunction Décalage d’indices En Scilab, les indices commencent à 0 et non à 1. On a déjà rencontré cette difficulté pour le calcul de la suite u(n), lorsqu’on a écrit : u=[u0];//initialisation for i=1:n u(i)=[u,f(u(i-1)];//ajout d’une composante au vecteur u end out=u(n+1); Du point de vue mathématique, on calcule un vecteur qui contient [u0 , . . . , un ], mais avec Scilab, les éléments du vecteur sont [u(1),u(2),u(3)... u(n+1)]. Ce vecteur est donc de taille n+1, et l’indice i en Scilab correspond donc à l’indice mathématique i − 1. Il faut donc veiller à ne pas faire d’erreur lors du passage entre les deux indices. 12. Ici aussi, voir la partie sur les matrices/vecteurs. On utilise simplement le fait que si u est un vecteur de taille n, [u,a] est un vecteur de taille n+1, obtenu en concaténant a au vecteur u. 13. Attention, la fonction suite ne sera alors valable que pour cette fonction f particulière. La fonction f n’est plus une entrée de la fonction suite. VII À savoir Utiliser l’aide MotCle L’aide en ligne est disponible via les commandes help NomFonction ou apropos Érreurs d’arrondis Scilab ne fait que des calculs approchés et fait donc des erreurs numériques. À chaque calcul, une erreur est faite. Un des buts des mathématiques est donc de contrôler la propagation de ces erreurs. Par exemple, supposons qu’on exécute les lignes de code : clear; x=0; for i=1:30 x=x+0.1; end En théorie, la valeur de x est alors 3, ce qui est d’ailleurs la valeur affichée par Scilab. Pourtant, le test x==3 renverra la valeur F. Les erreurs d’arrondis faites durant les 30 additions font que la valeur de x est différente de 3, ce qui peut se vérifier en utilisant la commande : format(25), qui permet de modifier la précision de l’affichage. La valeur de x avec cette précision est alors : x= 3.0000000000000013322676 . Autre exemple : si on demande à Scilab d’inverser une matrice, puis de multiplier la matrice par l’inverse, on n’obtient pas exactement l’identité. -->A=rand(4,4);A*A^{-1} ans = 1. 1.958D-16 2.147D-17 1.786D-16 1.471D-16 1. 1.269D-17 4.605D-17 1.718D-16 - 8.496D-17 1. - 1.455D-16 - 3.771D-16 - 2.040D-16 6.635D-17 1. Les termes hors diagonaux sont quasi nuls. Dernier exemple, la suite de commandes n=2;i=1 while (n<n^2) i=i+1; n=n^2; end disp(i) se termine et affiche une valeur de i. En effet, Scilab ne peut gérer des entiers trop grands. Quand utiliser un script et quand utiliser une fonction ? Pour comprendre la différence entre √ un script et une fonction, considérons, par exemple, le cas du calcul de a par la méthode de Newton, i.e. en itérant la fonction f (x) = 21 x + xa . La suite d’instructions : clear; n=10;//nombre d’itérations y=2; deff("y=f(x)","y=0.5*(x+ (a/x))"); for i=1:n y=f(y); end y //y est la valeur calculée √ Cette suite d’instructions peut être rentrée dans un script, qui permet alors de calculer 2, en itérant 10 fois la fonction f . Cette méthode peut servir de test, mais elle n’est pas très souple : – si on veut augmenter le nombre d’itérations, on est obligé de changer la valeur de n, puis de sauvegarder et de relancer le script avec exec. – idem, si on veut changer la valeur 2. – il faut savoir que y est la valeur calculée. Dans le cas où y est déjà une variable allouée, il faut remplacer (partout) y par z. Pour toutes ces raisons, il est préférable de rentrer ces valeurs dans une fonction. Cette fonction prend en entrée a et n et retourne la valeur trouvée de y 14 . Cette fonction s’écrit : function y=newton(a,n) y=a; deff("y=f(x)","y=0.5*(x+ (a/x))"); for i=1:n y=f(y); end endfunction Ensuite, cette fonction peut être appelée par une instruction du type f(2,10), f(3,20), etc. Le résultat peut être stocké dans une variable z, en posant z=f(3,10). De plus, si y a une valeur dans l’espace de travail, cette valeur n’intervient pas. Un autre exemple parlant est le cas des entrées sorties au clavier. Un script qui demanderait un entier en utilisant la commande input, calculerait et afficherait son carré, a un intérêt pédagogique, mais aucun intérêt pour représenter la fonction x 7→ x2 sur [0, 1], puisqu’il faudrait rentrer des centaines de valeurs à la main entre 0 et 1 et noter leur carré. Enfin, en informatique, on s’intéresse aux propriétés d’un algorithme comme celui de Newton. Par exemple, on se demande si, lorsqu’on augmente les itérations, on obtient vraiment une meilleure approximation. Ou, on veut savoir si il y a des valeurs de a pour lesquelles la convergence est plus ou 14. Généralement, les entrées et les sorties sont données par l’énoncé. Il faut respecter, dans ce cas, ce qui est demandé dans l’énoncé. moins rapide. Ainsi, il est conseillé d’écrire, en parallèle de la fonction, une « démo » de cette fonction, qui va être un script appelant la fonction sur diverses valeurs pour la tester. Par exemple, on écrira un script sous la forme : clear; exec("newton.sci"); //test avec a=2 et n=10 y=newton(2,10); disp(y,"valeur trouvée"); disp(abs(y-sqrt(2)),"erreur commise"); //test avec a=3 et n=10 y=newton(3,10); disp(y,"valeur trouvée"); disp(abs(y-sqrt(3)),"erreur commise"); Dans un exercice de Scilab, il est ainsi conseillé de fournir un couple script/fonction. La fonction contient l’algorithme demandé, et le script quelques tests permettant de prouver sa validité et de montrer son fonctionnement. Les fonctions Scilab à connaître Les fonctions à connaître sont : Interrompre le flux d’exécution Comme on l’a vu, si on exécute les commandes : i=0; while(i<>10) disp("coucou"); end on « bloque » le flux d’exécution dans la boucle while. L’exécution restant dans le bloc indéfiniment, scilab continue de tourner indéfiniment et est inutilisable. Pour arrêter le flux d’exécution, il faut alors utiliser le raccourci clavier CTRL+C, L’invite de commande devient alors -1->, on peut alors tout arrêter en utilisant la commande abort. Cette technique peut aussi être utilisée pour stopper la commande input. En complément, on peut utiliser la commande halt qui bloque l’exécution tant qu’une touche n’est pas appuyée, de manière par exemple à laisser le temps de lire sur l’écran. Pour utilisateurs avancés 15 , on peut utiliser ces fonctionnalités de débogueur en interrompant volontairement le flux d’exécution avec la commande pause. Une fois le flux d’exécution stoppé, on peut, par exemple, examiner le contenu des variables pour déterminer la source de l’erreur, puis, soit relancer le programme avec resume, soit le stopper avec abort. Par exemple, en utilisant un script contenant : clear; i=0; while (i<>10) disp("test") 15. Et uniquement eux. %i %pi %e clear who ans x=real(z) x=imag(z) x=conj(z) x=abs(z) x=phasemag(z) sqrt, exp, log sin, cos, tan round, floor, ceil disp("la valeur de x est "+string(x));) halt() x=input("question") x=input("question", "string") lines(0) save("sauv.sav") load("sauv.sav") help fonction apropos MotCle Nombre complexe i Nombre π Nombre e Efface toutes les variables Affiche les variables affectées Valeur du dernier calcul Partie réelle d’un nombre complexe z Partie imaginaire d’un nombre complexe z Conjugué d’un nombre complexe z Module d’un nombre complexe z / valeur absolue d’un nombre réel Argument en degré d’un nombre complexe Fonctions racine, exponentielle, et logarithme Fonctions sinus, cosinus, et tangente Fonction partie entière approchée, inférieure et supérieure Affiche la valeur de x est suivie de sa valeur, si x est un réel Attend l’appui sur une touche, très utile dans un script pour bloquer l’exécution entre chaque calcul Affiche la question et stocke la réponse dans la variable x, la réponse doit être un réel Affiche la question et stocke la réponse dans la variable x, la réponse doit être une chaîne de caractère Permet d’éviter les [More y or n] Permet de sauvegarder toutes les variables affectées, i.e. l’espace de travail Permet de restaurer les variables sauvegardées Aide sur la fonction fonction Recherche dans l’aide sur le mot clé MotCle Table 1.1 – Fonctions Scilab à connaître pause; end va arrêter le flux d’exécution au premier affichage de test. L’invite de commande permet alors de connaître la valeur de i, voire de la modifier avec [10]=return(i);, qui permet de revenir au programme avec une valeur modifiée de i. Affichage des résultats Lorsque vous affichez des résultats, Scilab va vous demander More y or n?. et vous devez appuyer sur y pour en voir davantage, sur n pour ne plus en voir. Pour empêcher ce comportement, il faut utiliser la commande lines(0);. Attention, le flux d’exécution est alors plus difficile à stopper. Application 1 Recherche d’un nombre par essais successifs Écrire un script qui : – tire un nombre entier x au hasard, entre 0 et 25. Pour cela, on utilisera l’instruction : x=round(25*rand(1,1)); Le but est que l’utilisateur trouve, par essais successifs, ce nombre x. – Le script demande un nombre à l’utilisateur, – puis affiche « Le nombre ... est supérieur au nombre cherché » ou « inférieur au nombre cherché » selon les cas, – et continue ainsi jusqu’à ce que x soit trouvé. – lorsque c’est le cas affiche « Félicitations, vous avez trouvé en .. essais » Chapitre 2 Types de données en Scilab Dans ce chapitre, on va s’intéresser aux types de données que peut manipuler Scilab. I I.1 Matrices et vecteurs Créer des matrices et des vecteurs en Scilab Donner les coefficients d’une matrice Pour rentrer une matrice en Scilab, on peut rentrer ses éléments un par un : par exemple : M=[1,2,3;3,2,1];, créera la matrice M : " # 1 2 3 M= . 3 2 1 On voit donc que , permet de changer de colonne et que ; permet de changer de ligne. On peut aussi remplacer ; par un retour à la ligne, et , par un espace. Il n’y a pas, par contre, de notion de vecteur : un vecteur est simplement une matrice possédant une ligne (vecteur ligne), ou une unique colonne (vecteur colonne). Si Scilab ne sait pas gérer les dimensions, il renvoie le message d’erreur : inconsistent row/column dimensions. Commandes sur les matrices Le tableau 2.1 récapitule les commandes à connaître pour la manipulation des matrices et des vecteurs. I.2 Manipuler les matrices et les vecteurs en Scilab Initialisation affectation Les matrices peuvent être stockées dans des variables et se manipulent exactement de la même manière. En fait en Scilab, toute variable est une matrice. Ainsi, si on définit la variable a par a=2, puis que l’on fixe la valeur ligne 3, colonne 4 de a comme égale à 5, Scilab « ajoute des 0 » à la variable a pour en faire une matrice à 3 lignes et 4 colonnes. Application 1 Si on tape la suite de commandeS : clear;x(5)=3; que contient la variable x ? 25 [n,m]=size(A) n=size(A,"r") m=size(A,"c") m=size(A,"*") m=length(v) x=linspace(a,b,n) x=a:p:y x=a:y rand(n,m) eye(n,n) ones(n,m) zeros(n,m) [ ] Taille d’une matrice A Nombre de lignes (row) d’une matrice A Nombre de colonnes d’une matrice A Nombre d’éléments d’une matrice A Nombre d’éléments d’un vecteur v Construit un vecteur ligne x contenant n points équidistants entre a et b Construit un vecteur ligne x qui contient les nombres de a à b par pas de p. Idem mais avec un pas de 1 par défaut Construit une matrice aléatoire de taille n par m Construit la matrice identité de taille n Construit une matrice ne contenant que des 1 de taille n par m Construit une matrice ne contenant que des 0 de taille n par m Matrice vide, permet en particulier d’initialiser sans valeur Table 2.1 – Manipulation des matrices et des vecteurs. Opérations mathématiques Les opérations mathématiques se font comme avec des variables classiques : – x*M permet de multiplier la matrice M par un scalaire x, – A*B permet de multiplier deux matrices A et B, – A+B permet de les additionner, – A^k permet de calculer Ak , en particulier A^(-1) permet d’inverser 1 la matrice A – A’ permet de calculer la transposée de la matrice A. Si les dimensions ne sont pas compatibles, on obtient des messages d’erreur comme : inconsistent addition, ou inconsistent multiplication. Enfin, notons que la suite de commandes : A=rand(4,4);A*A^(-1) ne renvoie pas exactement la matrice identité, mais quelque chose de très proche du fait des erreurs numériques. Il existe aussi une commande inv(A) pour calculer l’inverse. Manipulation des éléments, extraction de sous-matrices Pour avoir accès à l’élément ligne i colonne j, il suffit d’utiliser l’instruction : A(i,j). Ceci est valable en lecture autant qu’en écriture, i.e. pour modifier la valeur de l’élément qui est dans A. De même, pour un vecteur, la syntaxe est v(i) pour l’élément i. Attention, le premier élément est le 1, le dernier vaut la taille en ligne/colonne. Pour extraire une partie de la matrice, par exemple le vecteur des éléments lignes 1 à 3 de la colonne 2, on utilise : A(1:3,2). Pour extraire la ligne 3, on dispose d’un raccourci : A(:,3), idem pour la colonne 2 : A(2,:). Par souci de simplicité, un raccourci permet aussi d’obtenir le dernier élément : A(:,$) est ainsi la dernière colonne, tandis que v($) est le dernier élément de v. Ces matrices extraites peuvent être bien entendu stockées dans de nouvelles variables. 1. Ne pas oublier les parenthèses autour du -1. Enfin, comme pour les éléments, cette extraction est valable en lecture comme en écriture. Ainsi, par exemple, les commandes : A=rand(4,4); A(1,:)=1:4; A(:,2)=ones(4,1); permettent de créer une matrice aléatoire, puis de remplacer la première ligne par les entiers de 1 à 4. Enfin, elles remplacent la colonne 2 par des 1. Ceci peut aussi être utilisé pour supprimer une ligne ou une colonne en affectant la valeur matrice vide. Par exemple : A(1,:)=[]; supprime la ligne 1 de la matrice A. Si on essaie de lire un élément qui n’existe pas, Scilab retourne un message d’erreur : invalid index. Par contre, si on essaie d’écrire sur un élément qui n’existe pas, par exemple si on écrit : A=rand(4,4); A(5,5)=3; Scilab ajoute des zéros à la matrice A de manière à pouvoir mettre l’élément (5,5) à 3. Note: L’extraction de sous-matrice peut même se faire avec une liste d’indices : A=rand(10,10);v=[1,3,5]; w=[4,6];B=A(v, la variable B contiendra alors les lignes 3 × 2 coefficients obtenus en prenant les éléments des lignes 1, 3 et 5 et des colonnes 4 et 6. Changement de taille Pour changer la taille d’une matrice, i.e. organiser les éléments d’une autre manière, on utilise la commande M=matrix(v,n,m), qui prend en entrée une matrice ou un vecteur v contenant nm éléments et sort la matrice M de taille n par m contenant les éléments de v rangés en colonnes. Exemple: -->x=1:10;matrix(x,5,2) ans = 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. -->matrix(x,2,5) ans = 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Concaténation de matrice/vecteur Pour concaténer des matrices et des vecteurs, on procède comme si il s’agissait de nombres. Ainsi, pour obtenir la matrice C, constituée de la matrice A audessus de la matrice B, il suffit de faire : C=[A;B]. Tandis que C=[A,B] permettra d’obtenir la matrice A à gauche de la matrice B. Exemple: Par exemple, on peut obtenir la matrice : 1 4 A= 7 1 1 à l’aide de la commande : 2 5 8 1 1 3 6 9 1 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 A=[matrix(1:9,3,3)’, eye(3,3);ones(2,3),zeros(2,3)] Notons que cette fonctionnalité est particulièrement utile lorsqu’on stocke le résultat dans la même matrice. Par exemple, on utilise : M=[M,x] pour ajouter à la matrice M la valeur de la variable x. Égalité On peut tester si deux matrices ou deux vecteurs sont égaux avec la commande isequal, qui renvoie F ou T. Pour comparer élément par élément, on peut utiliser simplement == ou >=, on obtient alors une matrice de F et de T, qui donne pour chaque élément, si il est égal ou supérieur à son correspondant. Opérations non mathématiques Scilab permet de faire des opérations non mathématiques, utiles dans la pratique pour manipuler rapidement les matrices. On dispose en effet de : addition matrice/scalaire Si x est un scalaire et M une matrice ou un vecteur, M+x permet d’ajouter à chaque élément de M la valeur de x, cela correspond donc 2 à M+x*ones(n,m), où n et m sont la taille de la matrice M. multiplication élément par élément Lorsqu’on dispose de deux matrices A et B de même taille, on peut parfois vouloir les multiplier élément par élément. Cela peut être fait par la commande A.*B qui évite ainsi le passage par une boucle for. Bien entendu, il faut que les tailles soient compatibles pour que cette opération ait un sens. Pratiquement, l’opération C=A.*B remplace la suite d’instructions : [n,m]=size(A); for i=1:n for j=1:m C(i,j)=A(i,j)*B(i,j); end end Fonctions de Scilab Le tableau 2.2 contient une liste des opérations sur les vecteurs/matrices déjà implémentées dans Scilab. Même si il ne faut pas connaître par cœur ces fonctions, il faut connaître l’existence de ces fonctions pour gagner du temps, en évitant de les réécrire à chaque fois. Il faut aussi être capable de retrouver sans erreur la syntaxe exacte en utilisant l’aide et des exemples. En particulier, attention aux arguments ’c’ et ’r’. Par exemple sum(A,’r’) est la somme sur les colonnes, le ’r’ signifiant qu’on obtient un vecteur ligne. Attention, la plupart de ces fonctions sont interdites pour l’option, elles ne sont donc données ici qu’à titre indicatif. Application de fonctions sur des matrices On peut appliquer toutes les fonctions mathématiques sur des matrices. Dans ce cas, c’est la matrice contenant chacun des éléments qui est renvoyée. Par exemple, on peut faire : A=rand(4,4); sin(A); 2. Ceci explique aussi pourquoi en mathématiques, il ne faut pas écrire M + 2 à la place de M + 2I, pour Scilab M+2 n’est pas M+2*eye(2,2), mais M+2*ones(2,2). qui calcule l’image par la fonction sinus des éléments de A, en évitant ainsi de faire une boucle sur les éléments. Ceci est particulièrement utilisé pour les graphiques. Par exemple : x=linspace(-10,10,100); y=sin(x)+cos(x); permet d’obtenir un vecteur y contenant l’image par la fonction x 7→ sin(x) + cos(x) de 100 points équidistants dans l’intervalle [−10, 10]. Ensuite, la commande 3 plot2d(x,y) permettra de dessiner cette fonction. max(M) max(M,’c’) max(M,’r’) min(M) mean(v) median(v) st_deviation sort(v) sort(M,’c’) sort(M,’r’) sortup(v) sum(v) sum(A,’c’) sum(A,’r’) prod(A) Maximum d’une matrice M Maximum sur chacune des lignes Idem pour les colonnes Idem pour le minimum Moyenne Médiane Déviation standard Trie un vecteur par ordre décroissant Trie chaque ligne de la matrice M. Idem pour les colonnes Idem par ordre croissant. Somme des éléments de v Somme de chacune des lignes de A Idem pour les colonnes Idem pour le produit Table 2.2 – Fonctions matricielles et vectorielles en Scilab Application 2 Taper les commandes et expliquer : A=[1,2,3;4,5,6] A=[1;2;3;4,5,6] //où est l’erreur? A=eye(3,3) A=zeros(4,5) A=ones(3,2) A=rand(2,2) t=1:10 T=0:0.1:3 x=linspace(-10,10,20); //linspace = équirépartis A=rand(3,4);B=rand(3,4); A+B; A’ //transposition. size(A) size(A,’r’) size(A,’c’) 3. Voir le prochain chapitre sur les graphiques. size(A,’*’) A=rand(3,4); B=[1,1]; A*B //produit [B,3] B=[B,B] //concaténation A*B //pourquoi cela provoque une erreur? A(1,1) A(1:3,2) A(:,3) //: =toutes les lignes /colonnes A(2,:) C=A(1:2,3:4) //extraction A(1:5,6:8) //où est l’erreur? A(1,1)=0; A //modification d’un élément A(1,:)=[] A($,2) //$ donne le dernier élément. A=rand(3,3); A=rand(3,3); B=rand(3;3); C= A^(-1)*B; A*C Application 3 II Créer deux matrices de taille 3 × 3, puis comparer A.*B et A*B. Expliquer. Chaînes de caractères En Scilab, les chaînes de caractères (string en anglais), i.e. les textes, sont des variables comme les autres. Les opérations sur ces objets sont simplement : l’initialisation, obtenue en faisant x="Lundi, Mardi, "; la concaténation, obtenue en faisant x+y la comparaison, obtenue en faisant x==y ou x<>y. Par contre x>y n’a pas de sens. Voir plus loin pour la comparaison à l’ordre lexicographique. la conversion, d’un réel en chaîne de caractères égale à sa valeur, par la fonction string(x), où x est une variable contenant un réel. l’évaluation, est l’opération inverse de la conversion. Elle prend en entrée une expression, par exemple "x+y" et renvoie le réel obtenu en évaluant cette expression, ici la somme de la variable x et de la variable y. Cette opération est obtenue à l’aide de evstr(c), où c est une chaîne de caractères. la longueur d’une chaîne , i.e. le nombre de lettres, est obtenu par la fonction length(c), où c est une chaîne de caractères. l’affichage L’affichage à l’écran se fait avec la commande disp. Par exemple : disp("Bonjour"). Cette commande est à conseiller pour afficher un mélange de contenu de variables numériques et de chaînes de caractères : par exemple : disp("la valeur de x est " + string(x)) L’extraction de sous-chaînes Si x est une chaîne de caractères, alors part(x,i) est le caractère situé à la position i, tandis que la commande part(x,1:3) renverra les trois premières lettres. Cela peut être étendu au cas d’un vecteur v contenant des entiers. La commande part(x,v) renverra les caractères aux indices donnés par v. La conversion en valeur ascii La conversion ascii permet de coder une lettre en un nombre qui indique sa position dans la table des caractères. Les 10 chiffres sont codés en premier (de 0 à 9), puis la lettre a est codé en 10, la lettre b en 11, etc. Les lettres majuscules sont codés avec un chiffre négatif : la lettre A est codé en -10, B en -11 etc. Cette conversion est faite avec les commandes str2code qui convertit une chaîne en un vecteur de codes, et code2str qui convertit un vecteur de codes en chaîne. Cela est surtout utilisé pour faire des opérations sur les chaînes faisant appel à l’ordre lexicographique (ex. décaler d’un certain nombre de lettres, classer par ordre alphabétique). Exemple: x="oui"; //on peut comparer des chaînes de caractères if (x=="non") then disp("Pourquoi?"); end //pour séparer la variable x et la chaîne de caractères x on utilise les " " z=bonjour;//renvoie un message d’erreur z="bonjour"; length(z)//nombre de lettres 2*z; //renvoie une erreur: on ne peut pas * des chaînes de caractères x="10";// une chaîne de caractères peut contenir un nombre. y=2*x;//mais on ne peut pas faire d’opération dans ce cas, //x ne contient pas la valeur 10 mais la chaîne de caractères 10 y=2*evstr(x);//ici on convertit d’abord x en nombre disp("la valeur de y est"+y);//erreur car y est un nombre //on ne peut pas additionner une chaîne de caractères et un nombre. disp("la valeur de y est"+string(y)); //par contre, on peut concaténer deux chaînes de caractères. z="bonjour" x=part(z,1);// x contient "b" y=part(z,1:3);//y contient "bon" Enfin, on peut créer des matrices ou des vecteurs contenant des chaînes de caractères. Par exemple, un carnet d’adresses pourra être représenté par une matrice à 6 colonnes, avec des entrées du type : ["Nom","Prénom","Adresse","Ville","Code postal","Téléphone"]; Chaque ligne représentera une entrée dans le carnet d’adresses, et on ajoutera des entrées avec par exemple : M=[M;"Hoche";"Lazare","73 avenue de Saint-Cloud", ... "Versailles","78000","01.30.84.08.50"]; À noter qu’une telle matrice ne peut pas contenir à la fois des chaînes de caractères et des réels. Par exemple : x=["test",2] renverra un message d’erreur, d’où l’utilisation de evstr(c) lorsqu’on a besoin de faire des calculs avec les champs. Note: Attention à une erreur classique : il ne faut pas utiliser d’apostrophe dans les chaînes de caractères, qui sont alors confondues avec les ". Par exemple : x="j’aime faire du Scilab"; renverra une erreur. Application 1 1. Écrire un script qui pose les questions : « Nom ? », puis « Prénom ? », et enfin « Année de naissance ? » puis affiche : « Bonjour ... agé(e) de .. ans » en indiquant les valeurs rentrées. 2. Écrire un autre script qui : – demande à l’utilisateur un nombre, – écrit « Le carré de ... est ... », en remplaçant les points par la valeur rentrée et son carré. 3. Modifier ce script pour qu’il pose à l’utilisateur la question « Voulez-vous continuer ? » et attende o ou n, toute autre réponse amenant à reposer la question jusqu’à ce que l’une de ces deux réponses soit entrée. Utilisation du codage ascii Le codage ascii doit être utilisé dès qu’on fait intervenir l’ordre lexicographique. Application 2 Codage César : Le codage César consiste à coder un mot, en décalant sa position dans l’alphabet d’un paramètre fixé appelé clé. Par exemple, le mot bcpst, avec un clé de 6 sera codé en hivza. Cela peut-être fait simplement en remarquant que : str2code(abc) renvoie le vecteur : [10, 11, 12], tandis que si la variable v vaut [16, 17, 18], on a : code2str(v) renvoie la chaîne de caractères ghi. – Écrire à partir de ces éléments une fonction prenant en entrée une chaîne de caractères et sortant une chaîne de caractères codées avec le codage de césar et avec la clé 6. – Écrire une fonction permettant d’effectuer le décodage. – Modifier ces fonctions de manière à prendre en entrée la clé. On ne prendra que des chaînes constituées de lettres minuscules, sans espace. Application 3 Écrire une fonction estAvant prenant en entrée deux chaînes de caractères a et b, et sortant un booléen égal à vrai si le mot a arrive avant dans le dictionnaire.