Informatique en CPGE Cours 1ère année
Transcription
Informatique en CPGE Cours 1ère année
Informatique en CPGE Cours 1ère année Serge Bays Lycée des Eucalyptus Nice 28 mars 2015 Table des matières 0.1 0.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 6 6 6 7 7 7 8 8 8 8 8 9 9 Architecture 1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Définition : Ordinateur . . . . . . . . . . . . . . . 1.1.2 Définition : Informatique . . . . . . . . . . . . . . 1.2 Histoire . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Première génération . . . . . . . . . . . . . . . . 1.2.2 Deuxième génération . . . . . . . . . . . . . . . . 1.2.3 Troisième génération . . . . . . . . . . . . . . . . 1.2.4 Quatrième génération . . . . . . . . . . . . . . . . 1.3 Architecture matérielle . . . . . . . . . . . . . . . . . . . 1.3.1 Architecture de Von Neumann . . . . . . . . . . . 1.3.2 Une machine . . . . . . . . . . . . . . . . . . . . 1.4 Fonctionnement . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Systèmes d’exploitation (OS = Operating System) 1.4.2 Organisation du disque dur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 10 10 10 10 10 11 11 11 11 11 11 13 14 14 Algorithmique et programmation de base (partie 1) 2.1 Introduction . . . . . . . . . . . . . . . . . . . . 2.2 Les Langages . . . . . . . . . . . . . . . . . . . 2.2.1 Constituants . . . . . . . . . . . . . . . 2.2.2 Langage de programmation . . . . . . . 2.3 Algorithme . . . . . . . . . . . . . . . . . . . . 2.3.1 Définitions . . . . . . . . . . . . . . . . 2.3.2 Exemple . . . . . . . . . . . . . . . . . 2.4 Eléments de base en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 16 16 16 17 17 17 17 0.3 0.4 1 2 Généralités . . . . . . . . . . . . . . . . . . . . . . . . Le contenu théorique . . . . . . . . . . . . . . . . . . . 0.2.1 Algorithmique . . . . . . . . . . . . . . . . . . 0.2.2 Machine . . . . . . . . . . . . . . . . . . . . . . 0.2.3 Information . . . . . . . . . . . . . . . . . . . . 0.2.4 Langages . . . . . . . . . . . . . . . . . . . . . La progression . . . . . . . . . . . . . . . . . . . . . . 0.3.1 Un système informatique - 2 semaines . . . . . . 0.3.2 Codage des nombres - 2 semaines . . . . . . . . 0.3.3 Algorithmique et programmation - 16 semaines . 0.3.4 Ingénierie numérique et simulation - 10 semaines 0.3.5 Bases de données - 6 semaines . . . . . . . . . . Le fonctionnement . . . . . . . . . . . . . . . . . . . . 0.4.1 Matériel . . . . . . . . . . . . . . . . . . . . . . 0.4.2 Logiciels . . . . . . . . . . . . . . . . . . . . . 0.4.3 L’évaluation . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . http://mathematice.fr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 18 19 20 Algorithmique et programmation de base (partie 2) 3.1 Boucles et itérations . . . . . . . . . . . . . . . . 3.1.1 Compteurs . . . . . . . . . . . . . . . . 3.1.2 Boucles itératives conditionnelle . . . . . 3.1.3 Boucles itératives . . . . . . . . . . . . . 3.1.4 Rupture de séquence . . . . . . . . . . . 3.2 Calculs . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Bibliothèque mathématique . . . . . . . 3.2.2 Nombres complexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 22 22 22 23 25 25 25 26 2.5 3 4 5 6 7 2.4.1 Objets, expressions et types numériques 2.4.2 Variables et affectation . . . . . . . . . 2.4.3 Type str et fonction input . . . . . . . . Instruction conditionnelle . . . . . . . . . . . . Représentation des nombres 4.1 Codage de l’information . . 4.2 Codages des nombres entiers 4.2.1 Entiers naturels . . . 4.2.2 Entiers relatifs . . . 4.2.3 Programmation . . . 4.3 Nombres réels . . . . . . . . 4.3.1 Codage . . . . . . . 4.3.2 Programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 28 28 28 30 31 31 31 32 Les fonctions 5.1 Définition d’une fonction . . . . 5.2 Espace et portée des variables . 5.2.1 Espace local . . . . . . 5.2.2 Portée d’une variable . . 5.2.3 Variables globales . . . 5.3 Algorithmes . . . . . . . . . . . 5.3.1 Solutions d’une équation 5.3.2 Recherche d’extremum . 5.3.3 Test de la monotonie . . 5.3.4 Intégrales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 34 35 35 35 36 36 36 37 38 38 Les types composés 6.1 Types composés . . . . . . 6.1.1 Les n-uplets . . . . 6.1.2 Les listes . . . . . 6.2 Les tableaux et les matrices 6.2.1 Création . . . . . . 6.2.2 Utilisation . . . . . 6.2.3 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 40 40 41 43 43 43 43 . . . . . . 45 45 45 46 46 46 47 . . . . . . . . . . . . . . . . . . . . . . Les fichiers 7.1 Gestion des fichiers . . . . . . 7.1.1 Ouverture d’un fichier 7.1.2 Fermeture d’un fichier 7.2 Ecriture et lecture . . . . . . . 7.2.1 Ecriture . . . . . . . . 7.2.2 Lecture . . . . . . . . Serge Bays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lycée Les Eucalyptus http://mathematice.fr 7.3 8 9 Fichiers binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algorithmes : validité et complexité 8.1 Validité d’un algorithme itératif . . . . . . . . . . . . . . . 8.1.1 Invariants de boucle . . . . . . . . . . . . . . . . 8.1.2 Terminaison . . . . . . . . . . . . . . . . . . . . . 8.1.3 Un autre exemple . . . . . . . . . . . . . . . . . . 8.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.1 Mesure du temps d’exécution . . . . . . . . . . . 8.2.2 Borne asymptotique supérieure . . . . . . . . . . . 8.2.3 Niveaux de complexité . . . . . . . . . . . . . . . 8.3 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . 8.3.1 Recherche séquentielle dans un tableau non trié . . 8.3.2 Recherche par dichotomie dans un tableau trié . . 8.3.3 Recherche d’un mot dans une chaîne de caractères 49 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 50 50 51 51 51 52 52 52 53 53 54 54 Résolution d’une équation : méthodes de dichotomie et de Newton 9.1 Recherche dichotomique . . . . . . . . . . . . . . . . . . . . . 9.2 Méthode de Newton . . . . . . . . . . . . . . . . . . . . . . . . 9.2.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.2 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.3 Cas général . . . . . . . . . . . . . . . . . . . . . . . . 9.3 Complément . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3.1 Méthode de la sécante . . . . . . . . . . . . . . . . . . 9.3.2 Optimisation avec eval et exec . . . . . . . . . . . . . . 9.4 Utilisation de la bibliothèque scipy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 56 57 57 57 59 60 60 60 60 . . . . . . . . . . . . . . . . . . . . . . . . 10 Résolution numérique d’équations différentielles : méthode d’Euler 10.1 Méthode d’Euler . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.1 Exemple 1 . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.2 Exemple 2 . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.3 Exemple 3 . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 Complément . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 62 63 63 63 64 64 11 Résolution d’un système linéaire inversible : méthode de Gauss 11.1 Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.1 Création . . . . . . . . . . . . . . . . . . . . . . . . 11.1.2 Opérations classiques . . . . . . . . . . . . . . . . . 11.2 Autres opérations . . . . . . . . . . . . . . . . . . . . . . . 11.2.1 Recherche du pivot . . . . . . . . . . . . . . . . . . 11.2.2 Echange de lignes . . . . . . . . . . . . . . . . . . 11.2.3 Transvection . . . . . . . . . . . . . . . . . . . . . 11.3 Algorithme du pivot de Gauss . . . . . . . . . . . . . . . . 11.3.1 Résolution d’un système triangulaire . . . . . . . . . 11.3.2 Programme final . . . . . . . . . . . . . . . . . . . 11.4 Utilisation de Numpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 68 68 69 70 70 71 72 72 73 73 74 . . . . 75 75 75 75 75 12 Bases de Données Relationnelles 12.1 Principes et architecture . . . . . . . 12.1.1 Le concept de client-serveur 12.1.2 Architecture trois-tiers . . . 12.2 Le modèle relationnel . . . . . . . . Serge Bays . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lycée Les Eucalyptus http://mathematice.fr 12.2.1 Les relations . . . . . . . . . . . . . . . . . 12.2.2 Notion de clé primaire . . . . . . . . . . . . 12.2.3 La normalisation relationnelle . . . . . . . . 12.3 Algèbre relationnelle . . . . . . . . . . . . . . . . . 12.3.1 Vocabulaire des bases de données . . . . . . 12.3.2 Opérateurs usuels sur les ensembles . . . . . 12.3.3 Opérateurs spécifiques aux bases de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 77 77 78 78 78 78 13 Le langage SQL 13.1 Les requêtes d’interrogation . . . . . . . . . . 13.1.1 La logique d’interrogation . . . . . . . 13.1.2 Les opérations de base . . . . . . . . . 13.1.3 Le champ calculé . . . . . . . . . . . . 13.1.4 Les fonctions d’agrégation . . . . . . . 13.1.5 Les clauses de regroupement . . . . . . 13.2 Les requêtes de présentation des résultats . . . 13.2.1 Renommage de colonne . . . . . . . . 13.2.2 Ajout de texte . . . . . . . . . . . . . . 13.2.3 Le tri . . . . . . . . . . . . . . . . . . 13.3 Les requêtes de modification . . . . . . . . . . 13.3.1 Les requêtes d’insertion de données . . 13.3.2 Les requêtes de mise à jour de données 13.3.3 Les requêtes de suppression de données 13.4 Définition des données . . . . . . . . . . . . . 13.4.1 Suppression d’une table . . . . . . . . 13.4.2 Suppression d’un attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 80 80 80 82 82 82 83 83 83 83 83 83 84 84 84 84 84 14 Les bibliothèques Numpy, Matplotlib, Scipy 14.1 Calculs avec des tableaux . . . . . . . . . 14.2 Tracé de courbes . . . . . . . . . . . . . 14.3 Algèbre linéaire . . . . . . . . . . . . . . 14.4 Calcul d’intégrales . . . . . . . . . . . . 14.5 Equation f(x)=0 . . . . . . . . . . . . . . 14.6 Equations différentielles ordinaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 85 86 89 90 90 91 Serge Bays . . . . . . 4 . . . . . . . . . . . . Lycée Les Eucalyptus http://mathematice.fr Introduction 0.1 Généralités Depuis la rentrée 2013, l’informatique est devenue une discipline à part entière dans les Classes Préparatoires aux Grandes Ecoles. L’informatique et la numérisation sont omniprésentes dans la vie publique et pour les ingénieurs, enseignants et chercheurs dans leur vie professionnelle. A partir de quelques concepts fondamentaux, on réussit à conserver, à modifier et à transmettre toutes sortes de données à l’aide simplement de 0 et de 1. Un même appareil permet actuellement de chercher et stocker des informations, de lire des livres, d’effectuer des calculs, de regarder la télévision, d’enregistrer et d’écouter de la musique, de prendre des photos ou des vidéos et de les visionner et même de téléphoner. Il y a quelques années, pour chacune des ces fonctions, un appareil différent était nécessaire. L’algorithmique et le développement de programmes informatiques sont entrés dans toutes les disciplines scientifiques. L’informatique joue un rôle semblable à celui des mathématiques avec son développement propre et ses applications trans-disciplinaires. L’objectif principal de cet enseignement est de développer des compétences dans l’analyse et la modélisation d’un problème, d’une situation, dans la conception d’un algorithme et sa programmation afin d’obtenir une solution et dans la communication qui permet le partage du travail effectué et des résultats obtenus. 0.2 Le contenu théorique Mon premier ordinateur portable (début des années 1990) avait la même taille que ceux d’aujourd’hui. Mais à l’intérieur, le disque dur pouvait seulement contenir l’équivalent de deux minutes de musique (ou de vingt minutes compressées en mp3) ; Windows en occupait le tiers, Word un quart et le reste permettait d’avoir deux ou trois jeux et quelques fichiers. La mémoire était si petite que je ne m’en souviens plus. Ah oui, l’écran était en noir et blanc. Actuellement les ordinateurs contiennent dix mille fois plus de données, travaillent beaucoup plus vite, mais le fonctionnement est essentiellement le même ; il reste basé sur quatre concepts fondamentaux qui existaient déjà avant la naissance de l’informatique et qui sont : Algorithme, Machine, Langage et Information. 0.2.1 Algorithmique Algorithmes de base L’algorithmique est au programme de mathématiques à partir de la classe de seconde avec des algorithmes simples utilisant les notions de boucle, d’itération, de condition. Algorithmes de recherche et de calcul On développe des algorithmes de recherche dans une liste, un tableau trié, une chaîne et des algorithmes de calcul (moyenne, variance, zéro d’une fonction, valeur approchée d’une intégrale). On étudie la complexité en mémoire ou en temps, la vitesse d’exécution d’un algorithme sur des exemples simples et la notion d’invariant de boucle est introduite afin de vérifier la correction de segments itératifs. On s’efforce de bien distinguer les concepts "objet" algorithme et "outil" environnement de développement de leur "mise en œuvre" respective par l’implémentation dans un langage de programmation comme Python et l’utilisation d’une interface graphique permettant d’éditer un texte (code source), de l’interpréter, d’exécuter le programme, comme Idle pour Python. Serge Bays 5 Lycée Les Eucalyptus http://mathematice.fr 0.2.2 Machine Architectures matérielles Elles peuvent concerner différents types de machines : ordinateurs, réseaux, téléphone, télévision, caméra, appareil photo, baladeurs, robots, . . . Il s’agit d’identifier des composants de base tels que source d’énergie, mémoire, horloge, unité centrale, processeur, (les portes booléennes sont à la base du fonctionnement). Un ordinateur, en général ne fonctionne pas tout seul ; des périphériques lui sont reliés par des ports de communication et il s’agit de connaître leur rôle. Fonctionnement Pour fonctionner, la machine utilise un langage composé de suites d’instructions simples. Les systèmes d’exploitation (Unix, Linux, Windows, Mac Os, avec leurs différentes versions), qui sont liés au processeur utilisé, permettent la communication entre la machine et l’utilisateur (qui peut être une autre machine) et donc la gestion des répertoires et des fichiers, des applications ou des processus et du matériel. 0.2.3 Information Comment est représentée puis stockée l’information, comment une machine la transforme, comment on la transmet ? Codage de l’information Puisqu’une machine (numérique) travaille avec des nombres, l’information doit être au préalable numérisée, que ce soit un nombre, un caractère, un texte, une image, un son, . . . . Mais une machine ne connaît que le "0" et le "1". Si toutes les informations sont codées avec des nombres entiers (y compris les approximations de nombres réels), il s’agit alors d’avoir une représentation de ces nombres qui permette de les coder à l’aide uniquement de "0" et de "1" pour pouvoir les stocker et les utiliser dans la machine ; c’est la représentation binaire que l’on étudie puis le principe de représentation des nombres réels. Stockage et traitement Les données sont organisées dans des fichiers sous des formats différents. Les fichiers sont classés sous forme d’arborescence avec dossiers, sous-dossiers, . . . . Ils peuvent aussi être compressés pour gagner de la place. On utilise les outils du système d’exploitation. Bases de données Lorsque les données sont en très grand nombre, l’utilisation de bases de données relationnelles devient indispensable et il est nécessaire d’avoir des outils de gestion performants. Les étudiants sont initiés à différents concepts et au langage de requêtes SQL dans une perspective applicative à l’aide d’une interface graphique. 0.2.4 Langages Langages De très nombreux langages permettent de communiquer avec une machine. On peut les répartir à différents niveaux entre le langage machine et le langage naturel. Un langage de programmation permet d’écrire un code source (compréhensible par l’utilisateur) qui sera traduit en langage machine (soit compilé, Serge Bays 6 Lycée Les Eucalyptus http://mathematice.fr ce qui crée un fichier exécutable ensuite par la machine, soit interprété et exécuté par la machine en même temps). Programmation On utilisera un environnement de développement (IDE = Integrated Development Environment, à ne pas confondre avec Integrated Drive Electronics = Lecteur Électronique Intégré qui est souvent un disque dur) qui est "Idle" pour le langage Python et qui permet de rassembler plusieurs tâches (création, édition, modification d’un fichier source, interprétation, exécution d’un programme). On insiste sur les différents types de données utilisées et la notion de variable ainsi que sur le rôle capital des fonctions. L’étudiant doit être capable de comprendre un programme, d’en concevoir à partir d’algorithmes divers (en particulier ceux étudiés dans la partie algorithmique), de corriger ou d’améliorer un programme existant et de se questionner sur la certitude qu’un programme fonctionne à toute occasion. 0.3 La progression 0.3.1 Un système informatique - 2 semaines Architecture matérielle Organisation : • processeur (calcul-contrôle) <–> bus <–> mémoire • avec entrées-sorties et une horloge • et l’utilisation de portes booléennes Les composants de base d’une machine numérique et leur rôle rexpectifs doivent devenir familiers aux étudiants. Système d’exploitation On s’intéresse aux logiciels d’exploitation, (installation, fonctionnement, manipulation) ; ce sont des "gros" programmes qui démarrent lorsqu’on allume la machine et permettent de gérer les périphériques, les applications, les fichiers. Environnement de développement Présentation de l’environnement Idle pour Python. (C’est une GUI = Graphical User Interface ou Interface Utilisateur Graphique qui pemet à l’utilisateur d’agir à l’aide de clics de souris plutôt qu’uniquement en mode texte). 0.3.2 Codage des nombres - 2 semaines L’information est numérisée (traduite sous forme d’une suite de nombres). Comment représenter ces nombres pour que la machine les comprenne ? Nombres entiers On étudie particulièrement la représentation binaire et on abordera le codage en hexadécimal. Nombres réels On étudie la représentation des nombres réels en "virgule flottante". Serge Bays 7 Lycée Les Eucalyptus http://mathematice.fr Conséquences On déduit des représentations précédentes des conséquences sur les limites et la précision des calculs. 0.3.3 Algorithmique et programmation - 16 semaines Algorithmes de base On revoit les algorithmes du programme de mathématiques en lycée. Programmation de base Les algorithmes sont transcrits dans un langage de programmation afin d’être exécutés sur machine. Le langage utilisé est Python. On étudie pour commencer des séquences d’instructions simples. Algorithme de recherche, de calcul On travaille sur des algorithmes plus complexes avec leur programmation et on étudie leur complexité et leur vitesse d’exécution. L’étudiant doit être capable de comprendre un programme simple, de le corriger ou de l’améliorer, de savoir le modifier en fonctions des besoins et d’en concevoir. Enfin, l’étudiant doit pouvoir se questionner sur la certitude qu’un programme fonctionne à toute occasion (tests). 0.3.4 Ingénierie numérique et simulation - 10 semaines La présentation de l’environnement de calcul numérique Scilab et des bibliothèques Numpy et Scipy pour Python (1 ou 2 semaines) sera faîte assez tôt dans l’année suivant les besoins des autres disciplines. Etude du développement d’algorithmes permettant la résolution numérique des problèmes étudiés et mis en équations en Chimie, Physique, Maths et SI. Les étudiants peuvent comparer leurs programmes avec ceux existants déjà. 0.3.5 Bases de données - 6 semaines On présente le fonctionnement des bases de données relationnelles utilisant le langage SQL. Ceci se fait par l’intermédiaire d’une interface graphique permettant d’appréhender plus simplement les différents concepts et leur mise œuvre. On étudie le vocabulaire, les différents opérateurs et le concept client-serveur. 0.4 Le fonctionnement Les activités pendant les deux heures d’informatique hebdomadaires consistent en des cours théoriques alternés avec des TP (en général, de la pratique sur ordinateur). 0.4.1 Matériel Chaque étudiant travaille soit sur un poste (du lycée ou personnel) pour certaines activités, soit sur papier, en particulier pour l’enseignement théorique. Chaque étudiant doit avoir une clé usb car il est nécessaire de stocker à plusieurs endroits (au lycée, sur une clé et "ailleurs") le travail effectué pour éviter des pertes éventuelles et irrémédiables de documents. Deux livres références des éditions EYROLLES disponibles au lycée : Informatique pour tous en classes préparatoires aux grandes écoles Informatique et sciences du numérique (manuel de spécialité ISN en terminale) Serge Bays 8 Lycée Les Eucalyptus http://mathematice.fr 0.4.2 Logiciels Tous les logiciels sont gratuits et disponibles en téléchargement afin que les étudiants puissent les utiliser en dehors de la classe. La liste suivante présente ce qui peut être utilisé et n’est pas exhaustive. – Programmation : Python (avec Idle). – Calcul numérique : Scilab ou Python avec Numpy, Matplotlib et Scipy. – Editeurs : EditHexa, Notepad++. – Navigateur : Mozilla Firefox. – Base de données : PHPMyAdmin (avec Base), ou Base de OpenOffice. – Composition de documents : distribution LaTeX avec TeXnicCenter (Windows) ou Kile (Linux). 0.4.3 L’évaluation L’année scolaire est découpée en deux semestres. L’évaluation s’organise autour de tests de durées variables tout au long de l’année sur papier ou sur machine, éventuellement sous forme de QCM, portant sur la théorie et sur la pratique. Serge Bays 9 Lycée Les Eucalyptus Chapitre 1 Architecture 1.1 Introduction Un système informatique se compose : • d’une partie matérielle (hardware) qui représente l’ensemble des composants de la machine, • d’une partie logicielle (software) constituée des logiciels s’exécutant sur le matériel. Les caractéristiques du matériel influent sur les performances des programmes et une bonne connaissance du fonctionnement interne de l’ordinateur permet de comprendre pourquoi certains algorithmes se révèlent efficaces alors que d’autres sont mal adaptés, par rapport à une architecture donnée, et comment en améliorer le fonctionnement. 1.1.1 Définition : Ordinateur Un ordinateur est une machine électronique conçue pour effectuer des calculs et traiter des informations de manière automatique. Le terme ordinateur a été inventé par Jacques Perret, professeur de philologie latine à la Sorbonne, à la demande d’IBM France en 1955. Un ordinateur est composé de plusieurs parties appelées : • composants (carte mère, microprocesseur, barrette de mémoire, carte graphique) • périphériques (disque dur, lecteur de DVD, clavier, souris, moniteur, ...). Un périphérique est éloigné de la carte mère alors qu’un composant est en contact direct avec elle. Pour certains, le terme périphérique fait plutôt référence à tout ce qui est externe au boitier : clavier, souris, moniteur, imprimante... ce qui se trouve à la périphérie. 1.1.2 Définition : Informatique Science de la recherche et du traitement de l’information effectué par un ordinateur. Elle comprend l’ensemble des activités consistant à collecter, organiser et traiter de manière automatique les données par un ordinateur. Le terme informatique a été créé en mars 1962 par Philippe Dreyfus (Directeur du centre national de calcul électronique de la société Bull dans les années 1950) à partir des mots information et automatique. En anglais on emploie les termes Computer Science ou Computer Engineering. 1.2 1.2.1 Histoire Première génération Les premiers ordinateurs datent des années 1940, par exemple L’ENIAC (Electronic Numerical Integrator Analyser and Computer) aux USA, qui pesait 30 tonnes et était utilisé par l’armée, (programmé par des femmes), ou bien le Z1, le Z2 et enfin le Z3, en Allemagne, programmables et utilisant le binaire. 10 http://mathematice.fr Leur technologie était basée sur des tubes électroniques qui prenaient une place importante et dégageaient beaucoup de chaleur. De plus ils coûtaient très cher. Leur puissance de calcul (unité : le Flop, floating point operation per seconde) était comparable à celle d’une petite calculette d’aujourd’hui. En 1951, c’est la conception du premier compilateur par Grace Hopper (1906 -1992), informaticienne américaine, qui conçoit également le langage COBOL en 1959. Elle est à l’origine de l’expression "bug informatique", (mention dans son journal de bord "first actual case of bug being found"). 1.2.2 Deuxième génération Vers la fin des années 1950, les tubes sont remplacés par des transistors avec un gain en puissance de calcul. La consommation électrique, la taille et le prix sont réduits. Les ordinateurs rentrent dans les universités. 1.2.3 Troisième génération Dans les années 1960, c’est l’invention du circuit intégré, (puce en français, chip en anglais). Un circuit remplace de très nombreux tubes ou transistors. La NASA (National Aeronautics and Space Administration) va pouvoir embarquer un ordinateur pour aller sur la lune. 1.2.4 Quatrième génération A partir de 1971, le coeur de l’ordinateur est un ensemble de circuits intégrés appelé processeur. Actuellement, la puissance de calcul d’un ordinateur personnel est d’environ 100 Gigaflops, (Giga = milliards). L’apparition des micro-ordinateurs a permis la démocratisation de l’informatique. 1.3 1.3.1 Architecture matérielle Architecture de Von Neumann L’architecture des ordinateurs actuels repose sur le modèle de Von Neumann. John Von Neumann (1903-1957) était un mathématicien américain d’origine hongroise. Il travailla comme consultant dans le projet ENIAC. Selon lui la mémoire de l’ordinateur, qui servait à stocker des données, devait également stocker les programmes : c’est le concept de programme enregistré. L’organisation était la suivante : • une mémoire • une unité de calculs CA (Central Arithmetical part) que nous appelons de nos jours ALU (Arithmetic and Logic Unit) • une unité de contrôle CC (Central Control device) • des entrées/sorties • une horloge La mémoire stocke des nombres (Standard numbers) et des instructions (Orders) sur 32 bits (64 bits pour les ordinateurs les plus récents). Le bit 31 vaut 0 s’il s’agit d’un nombre et 1 dans le cas d’une instruction. On distingue aujourd’hui la mémoire dite centrale (RAM = Random Access Memory) de la mémoire de masse (qui correspond aux périphériques tels que les disques durs). Le processeur (CA + CC) communique avec la mémoire et les entrées/sorties par des "bus". L’horloge est un circuit qui émet un signal périodique afin de synchroniser les circuits qui en ont besoin (en particulier les circuits mémoires). 1.3.2 Une machine • Le boîtier contient l’ensemble des composants, (également appelé Unité Centrale) ; c’est un élément important en raison de plusieurs facteurs : Serge Bays 11 Lycée Les Eucalyptus http://mathematice.fr – les composants électroniques dégagent de la chaleur qui doit être évacuée sinon on risque une surchauffe qui pourra engendrer des dégâts ; – certains composants comme les disques durs, les ventilateurs font beaucoup de bruit, une bonne isolation phonique apporte un certain confort. – le boîtier est devenu un élément de mode. • Un bloc d’alimentation convertissant le courant alternatif 220 V en courant continu 12 V et 5V. (Une batterie rechargeable sur les ordinateurs portables). • – – – – – – Les périphériques externes d’entrée/sortie : Moniteur Clavier Souris Enceintes Imprimante Graveur externe • – – – – – L’Unité Centrale : Carte mère Micro-processeur Mémoire (RAM) les périphériques internes : disque dur, lecteur DVD, carte graphique, carte réseau, . . . Ports de communication L’information est stockée en Mémoire : • mémoire vive (RAM) accessible en lecture / écriture • mémoire morte (ROM = Read Only Memory) accessible en lecture seule • supports de stockage de masse (Disque Dur, clé usb, CDRom, Bandes) Carte mère La carte mère est un circuit imprimé qui permet de mettre en contact physique les différents composants et périphériques, et en particulier les trois éléments principaux de l’architecture de Von Neumann que sont : le processeur, la mémoire, les entrées / sorties (périphériques). C’est l’un des éléments essentiels d’un ordinateur. Les différents composants de la machine sont reliés par des canaux de communication, appelés bus, permettant d’échanger l’information. La carte mère (et notamment son chipset) détermine : – la vitesse des différents bus – le type de processeur qui peut être utilisé et la gamme de fréquences – le type de mémoire qui peut être utilisé, ainsi que la taille maximale de la mémoire On trouve donc sur une carte mère : – le socket qui est le support où l’on connecte le processeur et qui détermine son type, – des connecteurs pour la mémoire qui déterminent le type de mémoire à utiliser ainsi que la taille de la mémoire maximale, – différents ports : • PCI (Peripheral Component Interconnect) pour les cartes d’extension graphique, son, réseau, • AGP (Accelerated Graphics Port) pour les cartes graphiques hautes performances, • IDE (Integrated Device Electronics) pour les périphériques internes, disques durs, lecteurs/graveurs CD/DVD, • USB (Universal Serial Bus), bus série externe destiné à remplacer et unifier les différentes connexions (clavier, souris, imprimante qui utilisent les ports parallèle, port série), Serge Bays 12 Lycée Les Eucalyptus http://mathematice.fr • FireWire . . . Notion de bus Dans l’architecture de Von Neumann les différents composants échangent de l’information à travers des canaux appelés bus. Un bus se décompose en 3 parties : • le bus d’adresses qui permet de spécifier à quelle adresse mémoire on désire accéder : • le bus de données qui permet de spécifier d’envoyer ou de recevoir une donnée : • le bus de commandes qui permet de spécifier si on effectue une lecture ou une écriture. Sa largeur, (en nombre de bits ou d’octets) indique le nombre de bits qui sont transférés en même temps et sa fréquence, (en hertz), indique la vitesse de transfert de l’information. La bande passante est : fréquence × largeur. Mémoires On distingue la mémoire vive (ou RAM) de la mémoire morte (ou ROM). La mémoire vive ou RAM (Random Access Memory) est une mémoire volatile : si l’on coupe l’alimentation, les données qu’elle contient sont perdues. On y stocke les programmes exécutés par le processeur et elle est accessible en lecture et en écriture. • les registres sont les éléments les plus rapides. Ils sont situés au niveau du processeur et servent au stockage des opérandes et résultats intermédiaires. • la mémoire cache est une mémoire rapide de faible capacité destinée à accélérer l’accès à la mémoire centrale en stockant les données les plus utilisées. • la mémoire centrale contient les programmes (code + données) et est plus lente que les deux mémoires précédentes. • La mémoire d’appui est l’équivalent de la mémoire cache pour la mémoire de masse. • la mémoire de masse est un support de stockage généralement de grande capacité et sert au stockage et à l’archivage des informations. Les mémoires mortes ou ROM (Read Only Memory) sont des mémoires non volatiles auxquelles on accéde uniquement en lecture. Elles contiennent du code et des données qui ne sont pas amenés à changer souvent. Les ROM contiennent généralement les routines d’accès de base aux périphériques. Dans les ROM classiques, l’information contenue est enregistrée de manière irréversible lors de la fabrication du circuit. Le disque dur (hard drive) est un exemple de mémoire de masse, c’est l’un des composants (ou périphérique) de l’ordinateur servant à conserver les données de manière persistante, contrairement à la mémoire vive, qui est volatile. On qualifie également les média de stockage de grande capacité (clé usb) de mémoire de masse. 1.4 Fonctionnement Une machine exécute ce qu’on lui demande de faire et elle le fait sans aucune intelligence. Elle ne comprend que le langage "binaire" et on communique avec elle en utilisant un langage de programmation puis en passant par un "traducteur" (compilateur ou assembleur). On sait représenter un nombre en base deux et on sait que dans un circuit avec un interrupteur, soit il passe du courant si l’interrupteur est fermé, soit il n’en passe pas si l’interrupteur est ouvert. On peut rajouter une lampe dans ce circuit et alors si la lampe est allumée, cela veut dire "un", et si elle est éteinte cela veut dire "zéro". Ainsi une lampe correspond à un bit. On pourra donc représenter physiquement un nombre avec plusieurs circuits en parallèle. Serge Bays 13 Lycée Les Eucalyptus http://mathematice.fr 1.4.1 Systèmes d’exploitation (OS = Operating System) Un système d’exploitation est un ensemble de programmes qui sont lancés lorsqu’on allume un ordinateur. Tous les systèmes d’exploitation sont basés sur des concepts communs : des objets (les répertoires et fichiers, les processus, le matériel interne et périphérique, . . . ) chacun avec son outil respectif (gestion des fichiers, gestion des processus, gestion des périphériques). La mise en œuvre de ces concepts dépend du système d’exploitation. Pour Windows, nous avons les formats Fat32 et NTFS avec l’outils "Explorateur de fichiers", les applications, processus ou services avec l’outil "Gestionnaire de tâches", les cartes graphiques, les moniteurs, le réseau avec l’outil "Panneau de configuration". Le système d’exploitation permet donc de : • communiquer avec le disque dur afin d’y gérer les fichiers (leur attribuer un nom, les organiser en arborescence, . . . ). • gérer les périphériques à l’aide de "pilotes" (souris, écran, imprimante, . . . ) • exécuter simultanément plusieurs programmes (en partageant le temps alloué à chacun) • gérer l’authentification de chaque utilisateur et ses droits d’accès sur les fichiers (lecture, écriture, ...) 1.4.2 Organisation du disque dur Un programme permet d’installer sur le disque dur le système d’exploitation. Un partitionnement peut être effectué ; ceci consiste à partager le disque en plusieurs parties afin de séparer par exemple les programmes et les données. Le disque a subi un "formatage de bas niveau" en usine lors de sa fabrication. Ceci a pour but d’organiser la surface du disque en éléments simples : pistes (cylindres), secteurs qui permettront de localiser l’information. L’installation procède à un "formatage de haut niveau" qui permet d’organiser les pistes et secteurs en un système de fichier qui sera géré par un système d’exploitation (Windows, Linux, Unix, OS2, . . . ) : système de fichier NTFS pour Windows NT, XP, 7 ou 8 ; système Ext2, Ext3, pour Linux ... Le système d’exploitation nous permet de procéder à un formatage de haut niveau autant de fois que l’on veut par la suite. Durant le formatage on regroupe les secteurs en blocs. Un bloc ou "cluster" devient alors la plus petite unité d’allocation et on crée la FAT (File Allocation Table) qui contient la liste des clusters du disque ou de la partition. Avec le système NTFS, un cluster correspond à quatre Ko (Kilo-octet). Donc pour un fichier de 33,6 Ko, on réserve sur le disque 36 Ko soit 36 × 1024 = 36864 octets et pour un fichier de quelques octets, on réserve le minimum, soit 4,00 Ko (4 096 octets). Remarque : le partitionnement, effectué avant le formatage de haut niveau, permet de placer plusieurs systèmes de fichiers différents (donc plusieurs systèmes d’exploitation) sur le même disque en utilisant les différentes partitions. Le secteur de démarrage (MBR = Master Boot Record) est le premier secteur d’un disque dur (cylindre 0, tête 0 et secteur 1), il contient la table de partition principale et le code qui, une fois chargé en mémoire, va permettre d’amorcer le système (booter). Le MBR contient toutes les informations relatives au disque dur (fabricant, numéro de série, nombre d’octets par secteur, nombre de secteurs par cluster, nombre de secteurs, . . . ). C’est le secteur le plus important du disque dur, il sert au setup du BIOS à reconnaître le disque dur. Lorsqu’il est endommagé ou effacé par un virus le système d’exploitation ne peut plus démarrer. Serge Bays 14 Lycée Les Eucalyptus Chapitre 2 Algorithmique et programmation de base (partie 1) 2.1 Introduction Informatique : traitement automatisée de l’information. Algorithme : ensemble de règles opératoires dont l’application permet de résoudre un problème en un nombre fini d’opérations. Programme : séquences d’instructions et de données enregistrées sur un support et susceptibles d’être traitée par un ordinateur. Données : objets manipulés par le programme. Le programme est la traduction d’un algorithme dans un langage de programmation qui impose une syntaxe rigoureuse. Objectif : écrire un programme dans un langage donné dont l’exécution permet de résoudre un problème. Les étapes : A partir du problème à étudier, on commence par identifier et structurer les objets qui interviennent (les données), puis on détermine la méthode permettant de résoudre le problème (l’algorithme), et enfin on combine le tout en un programme. Lorsque l’algorithme est déterminé, il faut répondre à plusieurs questions avant de passer à la suite. - L’algorithme est-il correct, donne-t-il la bonne réponse dans tous les cas ? Ce n’est pas toujours facile de répondre à cette question. - Est-ce que l’algorithme est suffisamment efficace : cela demande une analyse de complexité. On se limitera aux cas faciles. - Est-ce que l’algorithme est facile à implémenter ? Un algorithme simple est toujours préférable. On peut alors passer aux instructions dans le langage de programmation choisi. Une fois le programme écrit, on effectue des tests afin de vérifier que son comportement est conforme à ce que l’on attend. Eventuellement on corrige et on recommence. En général, les erreurs de syntaxe, qui sont les plus courantes, seront détectées et le programme ne s’exécutera pas. Le problème de la sémantique est plus compliqué à régler ; le programme peut avoir un sens mais ne pas s’exécuter, ou ne jamais s’arrêter, ou encore fonctionner mais ne pas donner le résultat attendu et ce dernier cas est le pire. 15 http://mathematice.fr 2.2 2.2.1 Les Langages Constituants Une langue : - Syntaxe : des mots (noms, verbes, adjectifs, . . . ) et des règles de grammaire permettent de former des phrases. - Sémantique : une phrase bien formée n’a pas toujours un sens. Catégories de langages : - langue naturelle : syntaxe et sémantique ne sont pas rigoureuses. On peut se comprendre même avec des phrases pas toujours correctes. Le récepteur est un être humain intelligent. - langage informatique : syntaxe rigoureuse et rigide. La sémantique est complexe et difficile à bien appréhender. Le récepteur est une machine sans intelligence. - langage de description d’algorithme : une syntaxe moins rigide mais assez stricte pour ne pas donner d’ambiguité sur le sens de ce qui est écrit. 2.2.2 Langage de programmation Il existe des centaines de langages de programmation. Certain comme le C sont dits de "bas niveau", c’est-à-dire assez proche du langage binaire de la machine et donc assez complexe, d’autres comme Python sont dits de "haut niveau", plus éloigné du binaire donc plus souple, utilisant des opérations abstraites déjà prévues dans le langage. Le langage utilisé sera Python ; c’est un langage interprété, la séquence d’instructions ou code source est exécutée directement. D’autres langage sont compilés, le code est d’abord traduit en langage machine. Les programmes compilés sont en général plus rapide mais il est plus difficile d’y déceler d’éventuelles erreurs. Python est un langage général utilisable dans de nombreux domaines et portable (le même code source peut être interprété sous Windows, linux, Mac OS, . . . ). C’est un langage qui a beaucoup évolué depuis son introduction par Guido von Rossum en 1990. La dernière version est Python 3.4.1. Un langage de programmation comprend : – des commentaires : ils servent à expliquer (en langue naturelle) le comportement du programme et ne jouent aucun rôle dans le comportement du langage. En Python, un commentaire est un texte précédé du symbole #. Ce texte n’est pas interprété. – des noms appelés identificateurs : ils désignent des variables, des fonctions,. . . – des expressions : ce sont des combinaisons de noms avec des valeurs et des opérateurs. (par exemple "x+2") – des instructions : ce sont des commandes qui demandent de faire quelque chose. (par exemple "z=x+2" demande d’affecter à z la valeur de x+2). – l’indentation : cela consiste à laisser une ou plusieurs espaces au début des lignes. L’indentation permet la lisibilité du programme ; en Python, elle fait partie de la syntaxe et remplace l’utilisation d’accolades en C++ ou en Java. Un programme source est une phrase de ce langage. C’est une suite de caractères, (un programme écrit en Python est un script, soit une suite de définitions et d’instructions), pouvant être écrite dans un éditeur de texte et compréhensible par l’humain. Un interpréteur pour Python, ou un compilateur pour d’autres langages, permet ensuite de traduire et d’exécuter le programme sur une machine. Serge Bays 16 Lycée Les Eucalyptus http://mathematice.fr 2.3 Algorithme 2.3.1 Définitions Le mot "algorithme" vient du nom de l’auteur persan Al-Khuwarizmi Mohammed (né vers 780 - mort vers 850) qui a écrit en langue arabe le plus ancien traité d’algèbre (de l’arabe "al jabar" qui signifie "la transposition" et du grec "arithmos" qui signifie "nombre") dans lequel il décrivait des procédés de calcul à suivre étape par étape pour résoudre des problèmes ramenés à des équations. Un algorithme peut se définir comme étant l’ensemble des règles et instructions à suivre, effectivement exécutables, permettant d’obtenir un résultat clairement défini en un nombre fini d’étapes. Il ne faut pas confondre "algorithme et "programme". Un algorithme peut s’exprimer avec une notation indépendante de tout langage de programmation et il s’implémente, (c’est la "mise en œuvre"), dans un programme qui lui est écrit dans un langage particulier compréhensible par une machine. Un algorithme n’est pas compréhensible par un ordinateur qui ne peut qu’exécuter un programme. De plus, un algorithme doit toujours donner une réponse après un nombre fini d’opérations, alors que l’exécution d’un programme peut conduire à une boucle infinie et ne jamais s’arrêter. Un algorithme se compose de données et d’instructions. Dans les problèmes que nous allons résoudre, les données peuvent être de différents types. On rencontre les types nombre ou texte (chaînes de caractères), ou le type logique (vrai ou faux), ou le type graphique (des points). Les instructions à donner pour une exécution automatique doivent être suffisament simples pour être traduites dans un langage de programmation. Les instructions élémentaires sont : – les instructions d’entrée et de sortie : l’entrée (ou la lecture) des données peut se faire en interrogeant l’utilisateur ou par extraction à partir d’un fichier ; la sortie (ou l’écriture) peut se faire par un affichage sur l’écran, une impression sur papier, une écriture dans un fichier ou bien les résultats obtenus peuvent aussi rester en mémoire, (la sortie permet à l’utilisateur de voir les valeurs des variables après le traitement ou en cours de traitement si on veut contrôler l’exécution du programme) ; – les affectations et les calculs : l’affectation d’une donnée dans une variable consiste en la création d’une zone de mémoire (dans la machine) à laquelle on donne un nom, ensuite diverses formules permettent d’effectuer des calculs à l’aide d’opérations élémentaires, par exemple pour déterminer la valeur d’une fonction pour une valeur donnée de la variable. 2.3.2 Exemple Ecrire un algorithme de résolution dans R d’une équation du second degré. 2.4 Eléments de base en Python Lorsqu’on ouvre Idle pour Python, on peut écrire directement des instructions qui sont interprétées à chaque retour à la ligne. On peut aussi ouvrir une nouvelle fenêtre en allant dans le menu File, puis New Window, (ou au clavier avec "ctrl + N") et entrer toutes les instructions souhaitées. On enregistre ensuite le fichier par Save As . . . ; une fenêtre s’ouvre, où l’on choisit le dossier d’accueil et on entre un nom pour le fichier avec l’extension .py ("nom.py"). On peut alors lancer le programme en allant dans le menu Run, puis Run module ou en appuyant sur la touche F5 du clavier. En sortie d’un programme, on peut écrire les résultats obtenus dans un fichier ; ceci sera étudié plus tard. On peut aussi les afficher sur l’écran avec la fonction print. Par exemple print (’bonjour’) ou Serge Bays 17 Lycée Les Eucalyptus http://mathematice.fr print (25+36) ou encore print (’La somme est égal à’,25+36) qui affiche "La somme est égale à 61". 2.4.1 Objets, expressions et types numériques Un programme en Python manipule des objets. Chaque objet a un type qui indique l’ensemble des valeurs dont ils font partie et l’ensemble des propriétés qui les caractérisent. C’est le type qui définit ce que le programme peut faire avec un objet. Les types peuvent être scalaires ou non scalaires. Pour les scalaires, il y a : • le type int (abréviation de l’anglais integer), pour représenter les nombres entiers illimités et on écrit par exemple 5 ou 23214 ou −7 ; • le type float pour représenter les nombres réels et on écrit par exemple 5.2 ou −26.721. Les nombres du type float sont stockés dans la machine sous la forme de "nombres en virgule flottante" entre −1, 7 × 10308 et 1, 7 × 10308 , ceci sera étudié plus tard. • le type bool pour représenter les valeurs booléennes True et False ; • le type None qui n’a qu’une seule valeur. On forme des expressions en combinant les objets avec des opérateurs ; par exemple 5.2+3.4 représente l’objet 8.6 de type float. L’opérateur == est utilisé pour déterminer si deux expressions sont égales et l’opérateur != pour déterminer si deux expressions ne sont pas égales. La fonction type permet de déterminer le type d’un objet : >>>type(5.2) <class ’float’> >>> type(-18) <class ’int’> Les opérateurs sur les types int et float sont : – l’addition + , la soustraction - , la multiplication * , l’exponentiation ** dont le résultat est un float si l’un des termes est un float et sinon un int ; – la division / donc le résultat est toujours un float ; – la division entière // , l’opération modulo % : utilisés avec des nombres entiers, on obtient le quotient et le reste, de type int, dans la division euclidiennne : 17//5=3 et 17%5=2 ou (-17)//5=-4 et (-17)%5=3. Autre exemple : 7.4//3=2.0 (le résultat est un nombre entier mais de type float). Les opérateurs précédents suivent les règles de priorité habituelles et on peut naturellement utiliser des parenthèses. Il existe aussi des opérateurs de comparaison qui ont le sens usuel : < , <= , > , >= . Pour le type bool les opérations sont : – a and b donne True si a et b sont True et False sinon ; – a or b donne False si a et b sont False et True sinon ; – not a donne True si a est False et False si a est True. 2.4.2 Variables et affectation Une variable permet d’associer un nom avec un objet. Si on écrit pi = 3.14159, on lie le nom pi à un objet de type float ; on a alors une variable dont le nom est "pi" et la valeur est 3,14159. Une affectation associe le nom à gauche du signe = avec l’objet représenté par l’expression qui est à droite du signe = ; par exemple aire=pi*5**2. Serge Bays 18 Lycée Les Eucalyptus http://mathematice.fr Afin de faciliter la lecture d’un programme, il est important de bien choisir les noms utilisés. On peut écrire : a=3.14 b=5.3 c=a*b**2 mais il est préférable d’écrire, en ajoutant des commentaires si nécessaire : # Calcul de l’aire d’un disque pi=3.14 rayon=5.3 aire=pi*rayon**2 Il est important d’ajouter des commentaires qui seront ignorés par le compilateur mais qui seront bien utiles à la compréhension si quelqu’un lit le programme ou si on le relit dans trois mois. Pour cela on utilise le symbole # et le texte qui suit jusqu’à la fin de la ligne n’est pas interprété par Python. Python autorise des affectations multiples : x, y = 3, 7 associe x à 3 et y à 7. Ceci permet de faire l’échange : x, y = y, x de manière très simple (et bien utile !). 2.4.3 Type str et fonction input Type str Python n’a pas de type pour les caractères. Le type str, (abréviation de l’anglais string, chaîne en français), est utilisé pour représenter les chaînes de caractères. (Un caractère est une chaîne de longueur 1). On peut écrire ’bonjour’ ou "bonjour". Si on tape 3*’a’, on obtient ’aaa’ et si on tape ’a’+’b’ on obtient ’ab’. On dit que les opérateurs + et * sont surchargés, cela signifie qu’ils ont une action différente en fonction du type des objets auxquels on les appliquent. Ici, ce n’est plus l’addition et la multiplication des nombres mais + permet la concaténation de deux chaînes, * permet la répétition d’une chaine. La fonction len permet de trouver la longueur d’une chaîne : len(’bonjour’) donne 7. L’indexation permet d’extraire un caractère d’une chaîne. Attention, le premier caractère est indexé par 0, donc ’bonjour’[2] donne ’n’, soit le troisième caractère. On peut aussi indexer à partir de la fin de la chaîne : par exemple ’bonjour’[-1] donne ’r’. Pour extraire une sous-chaîne, on utilise ’chaîne’[début:fin] ; l’extrait commence à l’index début et se termine à l’index fin-1 : par exemple, ’bonjour’[2:5] donne ’njo’. Et donc ’bonjour’[0:len(’bonjour’)] donne ’bonjour’. Fonction input La fonction input permet d’obtenir une chaîne de caractères entrée par l’utilisateur. Le code suivant explique l’utilisation : >>>nom=input(’Entrer votre nom :’) Entrer votre nom : toto >>> print(nom) toto Serge Bays 19 Lycée Les Eucalyptus http://mathematice.fr Afin de récupérer un nombre, il est nécessaire de procéder à une conversion : >>> a=input(’Entrer un nombre entier : ’) Entrer un nombre entier 5 >>> print(3*a) ’555’ >>> a=int(a) #conversion d’un type str en type int >>> print(3*a) 15 La ligne a=int(a) permet de convertir le caractère ”5" en un nombre entier égal à 5. On peut aussi écrire plus rapidement : a=int(input(’Entrer un nombre entier : ’)). 2.5 Instruction conditionnelle Dans tous les exemples précédents, chaque instruction est exécutée dans l’ordre d’apparition. Les programmes avec branchement apportent un intérêt plus important. L’un des plus simples utilise l’instruction conditionnelle : un test est effectué (une expression qui a la valeur True ou False) ; si la valeur est True, alors un bloc de code est exécuté et un bloc optionnel peut être effectué si la valeur est False. On utilise l’instruction "Si . . . alors . . . " , (If . . . then . . . ), ou bien l’instruction "Si . . . alors . . . Sinon" (If . . . Then . . . Else). L’algorithme correspondant s’écrit ainsi : Si <condition> alors ... Instructions A ... Sinon ... Instructions B ... La traduction de l’algorithme en Python est : if (condition) : action1 action2 action3 else : action4 action5 La condition peut s’écrire avec des opérateurs logiques, par exemple (a > 10 and a < 20). Remarque : l’indentation est sémantiquement importante ; Python est très particulier dans cette utilisation (la plupart des autres langages utilisent des parenthèses ou des accolades). Toutes les instructions au même niveau d’indentation appartiennent au même bloc. Les programmes suivants ne sont donc pas identiques ; les commenter. if x%2==0 : print ("x est pair") if x%3==0: Serge Bays 20 Lycée Les Eucalyptus http://mathematice.fr print ("x est divisible par 6") else : print ("x est impair") if x%2==0 : print ("x est pair") if x%3==0: print ("x est divisible par 6") else : print ("x est impair") Note : il existe une syntaxe plus compacte d’une alternative, par exemple : min=x if x<y else y Serge Bays 21 Lycée Les Eucalyptus Chapitre 3 Algorithmique et programmation de base (partie 2) 3.1 Boucles et itérations 3.1.1 Compteurs Considérons l’instruction : nombre=nombre+1 Cette instruction signifie : "évalue l’expression de droite et affecte la valeur obtenue à la variable de gauche". Ceci permet donc d’incrémenter une variable de une unité et s’utilise souvent dans des boucles. On écrit aussi (un peu moins lisible mais plus rapide) : nombre += 1 3.1.2 Boucles itératives conditionnelle On utilise l’instruction "Tant que" (While). Cette instruction s’emploie pour répéter une suite d’instructions lorsque le nombre de répétitions est inconnu. La suite d’instructions est répétée tant qu’une certaine condition est vraie : on commence donc par un test, si le test est True alors on parcourt le corps de la boucle une fois ; ensuite on réévalue le test. Ce processus est répété jusqu’à ce que le test soit False ; on passe alors à la suite du code. Un exemple d’algorithme : Variable x x ... Début Tant que <condition sur x> ... Instructions ... Fin Tant que L’implémentation en Python est : 22 http://mathematice.fr x= ... while (condition): action1 action2 action3 La condition sur x peut être par exemple : (x<=100 and x >10). Attention à toujours vérifier que la boucle ne va pas se répéter sans fin. Si la valeur de x n’est pas modifiée dans le corps de la boucle, le test sera soit toujours False et la boucle n’est jamais exécutée, soit toujours True et alors le programme ne se termine jamais. Par exemple, l’algorithme suivant est-il correct ? Entier i i= 1 Début Tant que i>0 i = i+1 Fin Tant que Que penser du programme suivant ? Et si on remplace la première ligne par x=1 ? x=1.0 y=x+1 while y-x==1: x=10*x y=x+1 print (’x =’,x, ’y =’,y) Si on exécute ce programme, on obtient x=1e+16 et y=1e+16, soit 1016 . Le même programme exécuté sur une calculatrice donne 1012 ou 1014 ou . . . , suivant les modèles. Explication : x est du type float. Or la taille et la précision des nombres de type float dans la machine est limitée, (le codage des nombres sera étudié plus tard) ; donc il existe un grand nombre K, par exemple K = 1016 , tel que 1 soit négligable par rapport à K et alors K + 1 est égal à K (pour la machine !). La suite de nombres ainsi construite, 1, 2, 10, 11, 100, 101, . . . est croissante et majorée par K ! Donc elle est convergente et le programme va bien se terminer. Par contre si x et y sont du type int, le programme ne se termine pas car en Python les entiers sont illimités. Application à la dichotomie On cherche un nombre qui appartient à un ensemble de nombres ordonnés. La dichotomie consiste à partager l’ensemble en deux ensembles de même taille puis à tester auquel des deux ensembles le nombre appartient ; on recommence alors avec le nouvel ensemble et on reproduit ce processus tant que la taille de l’ensemble est supérieure à la précision souhaitée. Cette méthode permet à chaque étape de diviser la taille de l’ensemble, par exemple l’amplitude de l’intervalle, contenant la solution par deux. Donc en dix étapes, la taille est environ mille fois plus petite puisque 210 ' 1000. 3.1.3 Boucles itératives On souhaite afficher la table de multiplication par 13 : Serge Bays 23 Lycée Les Eucalyptus http://mathematice.fr i= 1 Début Tant que i<=10 Afficher 13*i i = i+1 Fin Tant que Le programme en Python est le suivant : i=0 while i<=10: print(’13 fois’,i,’=’,13*i) i=i+1 L’itération se fait sur une suite d’entiers bien déterminés. Une autre manière de produire le même résultat est d’utiliser l’instruction "Pour" (For). Cette instruction s’emploie pour répéter par exemple n fois une suite d’instructions lorsque n est connu à l’avance (pour déterminer un tableau de n valeurs d’une fonction, pour le calcul de n termes d’une suite, . . . ). L’algorithme est alors le suivant : Entier i Pour i = 0 à 10 Début Afficher 13*i Fin Et l’implémentation en Python : for i in range (0,11): print(’13 fois’,i,’=’,13*i) On peut aussi écrire for i in range (11):, car s’il n’y a pas de premier argument pour range, le programme considère qu’il vaut 0. De manière générale, on écrit : for variable dans une séquence: action1 action2 action3 Par exemple avec l’instruction for i in range (100): , la variable entière i va prendre successivement les valeur 0, 1, 2, . . . , 99, donc la boucle va s’effectuer 100 fois. Voici une boucle qui calcule et affiche les carrés des entiers de 0 à 100 : for i in range (101) : print (i**2) Serge Bays 24 Lycée Les Eucalyptus http://mathematice.fr La variable peut parcourir non seulement des entiers successifs mais aussi n’importe quel "itérable", (suite ordonnée d’éléments), par exemple : >>> for lettre in ’bonjour’: print(lettre, end=’ ’) b o n j o u r 3.1.4 Rupture de séquence Une boucle peut être interrompue par l’instruction break : for x in range (0,11): if x==5: break print(x, end=’ ’) 0 1 2 3 4 Une boucle peut "sauter" une valeur avec l’instruction continue : for x in range (0,11): if x==5: continue print(x, end=’ ’) 0 1 2 3 4 6 7 8 9 10 3.2 3.2.1 Calculs Bibliothèque mathématique De nombreux petits programmes souvent utilisés existent déjà et on les trouve dans des bibliothèques. Une bibliothèque pour Python (library en anglais, à ne pas confondre avec le mot français librairie qui se dit book-shop en anglais) est composée de modules ou packages. Le module math contient des constantes et des fonctions mathématiques usuelles. Pour une documentation, consulter http ://docs.python.org/3/ et vérifier en particuler le nom et l’utilisation des fonctions ; par exemple la fonction mathématiques ln est notée "log". Pour utiliser le module math, il faut l’importer dans notre programme : >>> import math >>> math.sqrt(2) 1.4142135623730951 >>> math.log(3) 1.0986122886681098 >>> math.exp(1) 2.718281828459045 >>> math.sin(math.pi/2) 1.0 Serge Bays 25 Lycée Les Eucalyptus http://mathematice.fr Afin de simplifier l’écriture des fonctions dans un programme, on peut utiliser une autre manière d’importer des fonctions du module math : >>> from math import sqrt, log, sin >>> sqrt(8) 2.8284271247461903 >>> log(5) 1.6094379124341003 >>> sin(1.57) 0.9999996829318346 On peut aussi utiliser l’instruction import math as m et le nouveau nom du module math dans notre programme est alors "m" ; ou bien l’instruction from math import* qui importe toutes les fonctions du module ; ceci peut être pratique mais cela signifie aussi que notre programme est rempli d’un grand nombre de noms que nous n’utiliserons pas. Enfin, on peut aussi renommer les fonctions en écrivant par exemple : >>> from math import log, e >>> ln=log >>> ln(e) 1.0 Note : les fonctions permettant de générer des nombres aléatoires sont dans le module random ; la fonction random() génère un flottant aléatoire dans l’intervalle [0; 1[, la fonction randint(a,b) avec a et b entiers génère un entier aléatoire n tel que a≤ n ≤b . On peur voir la doc http ://docs.python.org/3/library/random.html pour plus d’informations. 3.2.2 Nombres complexes Python permet d’effectuer des calculs avec des nombres complexes. Le nombre imaginaire noté i en mathématiques s’écrit j en Python et le nombre complexe 2 − 3i s’exprime par (2-3j) en Python. Attention, le nombre complexe i s’exprime en Python par 1j (pas seulement j). Exemple d’écriture : >>> c1=5-3j >>> c2=3+1j >>> p=c1*c2 >>> print (p) (18-4j) >>> type (p) <class ’complex’> >>> abs(3+4j) 5.0 On peut également définir un complexe par sa partie réelle et sa partie imaginaire, ou déterminer les parties réelle et imaginaire d’un complexe donné ainsi que son conjugué : >>> a=4 >>> b=-5 >>> c=complex(a,b) Serge Bays 26 Lycée Les Eucalyptus http://mathematice.fr >>> c (4-5j) >>> c.real 4.0 >>> c.imag -5.0 >>> c.conjugate() (4+5j) Il existe aussi un module mathématique consacré aux nombres complexes, le module cmath. Serge Bays 27 Lycée Les Eucalyptus Chapitre 4 Représentation des nombres 4.1 Codage de l’information Les ordinateurs, comme d’autres appareils, permettent de mémoriser, de transmettre et de transformer des nombres, des textes, des images, des sons, etc. Ils fonctionnent avec des petits circuits électroniques qui ne peuvent être, chacun, que dans deux états représentés par 0 ou 1. Ce qui correspond à fermé ou ouvert, faux ou vrai, . . . Une telle valeur, 0 ou 1, s’appelle un booléen, un chiffre binaire (nombre en base 2) ou encore un bit (binary digit), de symbole b. La mémoire des ordinateurs contient une multitude de ces circuits à deux états appelé circuit mémoire un bit. Un circuit composé de plusieurs de ces circuits mémoire un bit, se décrit par une suite finie de 0 et de 1, que l’on appelle un mot. Un mot de 8 bits est un octet. Le byte, de symbole B, ou multiplet, est la plus petite unité adressable d’un ordinateur et correspond en général à un octet, (soit 1 B = 8 b). Un nombre réel peut-être approximé avec la précision souhaitée par un nombre décimal qui peut s’exprimer à l’aide de nombres entiers. Pour coder un caractère, nous attribuons un nombre à chaque caractère selon des normes précises. Ensuite, un texte est une suite de caractères, donc est codé par une suite de nombres. Une image sur un écran d’ordinateur est composée de pixels (picture elements). Ce sont des petits carrés qui composent un quadrillage. Si l’image est en couleurs, trois paramètres codent le niveau de rouge, de vert et de bleu. C’est le système RGB, (red, green, blue) ou système RVB en français. Chacune de ces trois composantes est codée par un nombre de 0 à 255. De même il est possible de coder un son par une suite de nombres. Donc de manière générale, l’information peut être codée par des nombres, c’est-à-dire "numérisée". Il ne reste plus qu’à savoir représenter un nombre entier par une suite composées de 0 et de 1. Et un fichier stocké sur un ordinateur ne sera qu’une suite plus ou moins longue composées de 0 et de 1. 4.2 4.2.1 Codages des nombres entiers Entiers naturels Représentation en base dix On utilise pour les nombres entiers naturels la notation décimale à position. Pour cela, dix chiffres sont nécessaires et suffisants : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Par exemple, 3207 représente 3 milliers + 2 centaines + 0 dizaines + 7 unités. 3207 = 3 × 103 + 2 × 102 + 0 × 101 + 7 Pratiquement, si on dispose de 3207 objets identiques, et que l’on constitue des paquets de 10 objets, on 28 http://mathematice.fr en obtient 320 et il reste 7 objets. Ensuite, avec les 320 paquets on forme des ensembles de 10 paquets, on en obtient 32 ; puis on regroupe les 32 ensembles par 10, on obtient 3 groupes et il reste 2 ensembles. On utilise la base dix tous les jours, mais d’autres bases sont envisageables. La base deux En base deux il n’y a que deux chiffres, 0 et 1, et le principe reste le même qu’en base dix. Par exemple, pour 47 : on obtient 23 paquets de deux et 1 unités, puis les 23 paquets se regroupent en 11 paquets de paquets et il reste 1 paquets, ensuite . . . 47 = 23 × 2 + 1 = (11 × 2 + 1) × 2 + 1 = ((5 × 2 + 1) × 2 + 1) × 2 + 1 = . . . 47 = 1 × 25 + 0 × 24 + 1 × 23 + 1 × 22 + 1 × 21 + 1 Donc le nombre écrit 47 en base dix s’écrit 101111 en base deux. On peut utiliser la notation 1011112 . Dans la notation binaire : • le bit (ou chiffre) le plus à gauche est appelé bit de poids fort • le bit (ou chiffre) le plus à droite est appelé bit de poids faible Pour mesurer des quantité d’information, l’unité de base est le bit qui prend soit la valeur 0, soit la valeur 1. Un ensemble de 4 bits consécutifs est appelé un quartet, un ensemble de 8 bits consécutifs est appelé un octet (byte), deux octets consécutifs (16 bits) forment un mot (word). C’est l’octet qui est utilisé comme unité de référence pour mesurer la capacité des mémoires. Remarque : pour multiplier par deux un nombre écrit en base deux, il suffit d’ajouter un zéro à droite du nombre : 10112 × 102 = 101102 Une base quelconque On peut généraliser à une base quelconque les méthodes précédentes. Pour écrire les entiers naturels en base k, on a besoin de k chiffres. Quand on a n objets, on les groupe par paquets de k, qu’on regroupe à leur tour en paquets de k paquets, etc. Autrement dit, on fait une succession de divisions par k, jusqu’à obtenir un quotient égal à 0. Ici encore, la multiplication d’un nombre par k consiste à ajouter un zéro à droite du nombre. La base seize est particulièrement intéressante. On a besoin de 16 chiffres notés : 0, 1, 2, 3, 4, 5 , 6, 7, 8, 9, puis A (dix), B (onze), C (douze), D (treize), E (quatorze) et F (quinze). Si on travaille en base deux, l’écriture peut être très longue. Or un octet (huit bits) peut s’écrire simplement en base seize. On partage l’octet en deux et chaque partie de quatre bits s’écrit avec un chiffre de la base seize. Démonstration : le nombre seize s’écrit 10000 en base deux et 10 en base seize : 100002 = 1016 ; donc (a0 a1 a2 a3 a4 a5 a6 a7 )2 = (a0 a1 a2 a3 )2 × 100002 + (a4 a5 a6 a7 )2 = (b0 )16 × 1016 + (b1 )16 = (b0 b1 )16 Par exemple le nombre écrit 11010101 en base deux s’écrit D5 en base seize. En effet 11010101 se partage en 1101 et 0101 qui donnent respectivement D et 5 en base seize : 110101012 = 11012 × 100002 + 01012 = D16 × 1016 + 516 = D516 Dans l’ordinateur Dans un ordinateur c’est le codage binaire qui est utilisé. Par exemple avec le nombre dont la représentation décimale est 59 : 59 = 1 × 32 + 1 × 16 + 1 × 8 + 0 × 4 + 1 × 2 + 1 × 1 59 = 1 × 25 + 1 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 1 × 20 59 = 1110112 . Serge Bays 29 Lycée Les Eucalyptus http://mathematice.fr Avec des octets, soit huit bits, on peut représenter les entiers naturels de 0 (00000000 en base deux) à 255 (1111 1111 en base deux). Donc 59 sera représenté par 00111011. Si on utilise seize bits, soit deux octets, on peut représenter les entiers naturels jusqu’à 65535 (1111 1111 1111 1111 en base deux) et dans ce cas 59 sera représenté par 0000000000111011. Avec n bits, ce système permet de représenter les nombres entre 0 et 2n − 1, donc tout nombre k qui s’ecrit : k= n−1 X bi 2i bi ∈ {0, 1} i=0 Actuellement un entier est en général codé sur quatre octets (soit 32 bits). Addition de nombres binaires Pour additionner deux nombres binaires on procède comme en base 10. A partir de 0 + 0 = 0, 1 + 0 = 0 + 1 = 1 et 1 + 1 = 10 on pose l’addition comme en base dix et chaque fois qu’il y a 1+1, on écrit 0 et on retient 1. Par exemple : 1 1 1 0 1 vingt-neuf + 1 1 0 0 1 vingt-cinq 1 1 retenues 1 1 0 1 1 0 cinquante-quatre 4.2.2 Entiers relatifs On pourrait utiliser un bit pour le signe et les autres bits pour la valeur absolue. Mais cette méthode a des inconvénients. On a préféré la méthode de représentation par complément à deux qui permet de réaliser des opérations arithmétiques sans problème. Celle-ci consiste à réaliser un complément à un de la valeur, puis d’ajouter 1 au résultat. Par exemple sur 6 bits, pour obtenir le codage de −5 : 000101 (codage de 5 en binaire sur 6 bits) 111010 (complément à un, on inverse chaque bit) 111011 (on ajoute 1) : représentation de −5 en complément à deux sur 6 bits. Avec n bits, ce système permet de représenter les nombres entre −2n−1 et 2n−1 − 1, et tous les nombres négatifs ont le bit de poids fort (bn−1 ) égal à 1. Les entiers naturels de 0 à 2n−1 − 1 représentent les entiers relatifs positifs ou nul correspondants et les entiers naturels de 2n−1 à 2n − 1 représentent les entiers relatifs négatifs de −2n−1 à −1. Les machines actuelles utilisent 32 ou 64 bits (4 ou 8 octets). Pour simplifier, avec un octet, soit huit bits, on peut représenter les entiers naturels n de 0 jusqu’à 255 et donc les entiers relatifs de −27 = −128 à 27 −1 = 127 ; les nombres n de 0 à 127, (de 0000 0000 à 0111 1111 en base deux), servent à représenter les entiers relatifs positif ou nul r avec r = n et les nombres n de 128 à 255, (de 1000 0000 à 1111 1111 en base deux), représentent les entiers relatifs négatifs r avec r = n − 256. Le nombre n = 127 représente l’entier relatif r = 127 ; le nombre n = 128 représente l’entier relatif r = 128 − 256 = −128 ; le nombre n = 255 représente l’entier relatif r = 255 − 256 = −1. Remarque 1 : un principe équivalent sur le cercle trigonométrique parcouru dans le sens direct de degré en degré est de n’utiliser que des nombres positifs, soit, dans l’ordre, 0, 1, 2, . . . , 178, 179, 180, 181, . . . , 358, 359 (ceci correspond aux entiers naturels), ou d’utiliser aussi des nombres négatifs, soit, dans l’ordre, 0, 1, 2, . . . , 178, 179, -180, -179, . . . , -3, -2, -1. Serge Bays 30 Lycée Les Eucalyptus http://mathematice.fr Remarque 2 : tous les nombres négatifs ont leur bit de poids fort égal à 1, alors que les nombres positifs ont leur bit de poids fort égal à 0. Remarque 3 : avec ce codage, si on peut faire effectuer par une machine l’addition de deux nombres et le passage à l’opposé d’un nombre, on peut lui faire effectuer toutes les opérations classiques. exemple sur un octet : 5 est codé par 00000101, -3 est codé par 11111101, donc l’addition 5+(-3) se fait en ajoutant les nombres bit à bit, soit (1)00000010 qui est bien 2. (On ne garde que 8 bits.) Remarque 4 : si on avait choisi de coder les négatifs avec simplement un bit de signe (0 pour + et 1 pour -), alors -3 serait codé par 10000011 et l’addition 5+(-3) bit à bit donnerait 10001000 qui coderait -8 ! 4.2.3 Programmation En Python, les entiers sont représentés par le type int (integer). Les plupart des processeurs calculent avec des nombres binaires de taille limitée à 32 bits (ou 64 bits pour les processeurs les plus récents). Les langages de programmation en général font de même et les calculs sur ces entiers, gérés directement par le processeur, sont très rapides. Python permet de calculer avec des entiers de taille illimitée ; il gère lui-même l’encodage des nombres pour des tailles plus grandes afin de ne transmettre au processeur que des nombres sur 32 bits et le programmeur n’a pas à s’en occuper. La taille des entiers utilisés par Python n’est donc limitée que par la quantité de mémoire disponible dans l’ordinateur. Mais plus les nombres sont grands et plus les calculs vont être décomposés par l’interpréteur en calculs multiples sur des nombres plus simples et donc rallonger le temps de traitement. 4.3 Nombres réels 4.3.1 Codage Définition La norme IEEE 754 (Standard for Binary Floating-Point Arithmetic) définit la représentation de nombres réels appelés flottants ou nombres à virgule flottante. Un nombre réel est représenté par un nombre décimal, la meilleure approximation possible, qui s’écrit sous la forme s m × 2n ou s est le signe du nombre, n son exposant et m sa mantisse. Cette représentation est semblable à la notation scientifique des calculatrices, sauf qu’elle est en base deux et non en base dix. En faisant varier l’exposant n, on fait "flotter" la virgule. Le signe s est + ou −, l’exposant n est un entier relatif et la mantisse m est un nombre à virgule, compris entre 1 inclus et 2 exclu. (En notation scientifique, c’est un nombre à virgule compris entre 1 inclus et 10 exclu). Par exemple le réel 10 s’écrit +1, 25 × 23 ; le réel −0, 1875 s’écrit −1, 5 × 2−3 . Les flottants IEEE peuvent être codés sur 32 bits ("simple précision" avec 1 bit pour le signe, 8 bits pour l’exposant et 23 bits pour la mantisse) ou 64 bits ("double précision" avec 1 bit pour le signe, 11 bits pour l’exposant et 52 bits pour la mantisse). Si on code les nombres sur 64 bits : • Le signe + est représenté par 0 et le signe − par 1. • L’exposant n est un entier relatif compris entre −1022 et 1023 ; on le représente par l’entier naturel n + 1023, (exposant décalé), qui est compris entre 1 et 2 046. Les deux entiers naturels 0 et 2 047 sont réservés pour des situations exceptionnelles (+∞, −∞, NaN, etc). • La mantisse m est un nombre binaire à virgule compris entre 1 inclus et 2 exclu, comprenant 52 chiffres apres la virgule. Comme cette mantisse est comprise entre 1 et 2, elle a toujours un seul chiffre avant la virgule et ce chiffre est toujours un 1 ; il est donc inutile de le représenter et on utilise les 52 bits pour représenter les 52 chiffres apres la virgule. Dans l’ordinateur les nombres seront alors codés par : " signe exposant (décalé) mantisse (tronquée) " Serge Bays 31 Lycée Les Eucalyptus http://mathematice.fr ces trois parties étant codées en binaire. Exemple 1 : 10 = +1, 25 × 23 Le signe est + donc le premier bit vaut 0 ; l’exposant est 3 donc l’exposant décalé est 3 + 1023 = 1026 et en binaire cela donne 100 0000 0010 ; la mantisse est 1, 25 qui s’écrit en binaire 1,01. On garde la partie décimale 01 et on complète avec des zéros. Le codage de 10 est donc : 0 100 0000 0010 0100 0000 . . . 0000. Exemple 2 : −0, 1875 = −1, 5 × 2−3 Le signe est − donc le premier bit vaut 1 ; l’exposant est −3 donc l’exposant décalé est −3 + 1023 = 1020 et en binaire cela donne 011 1111 1100 ; la mantisse est 1, 5 qui s’écrit en binaire 1,1. On garde la partie décimale 1 et on complète avec des zéros. Le codage de −0, 1875 est donc : 1 011 1111 1100 1000 0000 . . . 0000. 4.3.2 Programmation En Python, les nombres réels ou nombres en virgule flottante sont du type float. Pour qu’une donnée numérique soit considérée par Python comme étant du type float, il suffit que sa formulation contienne un point décimal ou une puissance de 10 par exemple, 3.14 ou 1. ou .0001 ou 1e12 . Précautions d’emploi Les calculs en virgule flottante sont pratiques, mais attention : – leur précision est limitée ; cela se traduit par des arrondis qui peuvent s’accumuler de façon gênante. (Pour cette raison, en comptabilité les calculs ne sont pas effectués en virgule flottante). – il n’y a aucun nombre dans un voisinage suffisament petit de zéro : une représentation flottante possède une plus petite valeur négative, une plus petite valeur positive, et entre les deux le zéro, mais aucune autre valeur ! Que vaut dans l’ordinateur la plus petite valeur positive divisée par 2 ? Pour comparer deux nombres flottants on vérifiera donc que la valeur absolue de leur différence est inférieure ou égale à une précision donnée. – il y a un nombre maximal (max=1.7976931348623157e+308), donc une puissance de deux maximale (1023) et une puissance de dix maximale (308). >>> 1.0e308 1e+308 >>> 1.0e309 inf >>> 2.0**1023 8.98846567431158e+307 >>> 2.0**1024 Traceback (most recent call last): File "<pyshell#22>", line 1, in <module> 2.0**1024 OverflowError: (34, ’Result too large’) Remarque : un problème d’overflow est responsable de l’autodestruction de la fusée Ariane 5, le 4 juin 1996, 39 secondes après le décollage ; la forte accélération de la fusée a provoqué un dépassement de capacité lors du calcul des position et vitesse. Il peut être tentant de réorganiser des expressions en virgule flottante comme on le ferait d’expressions mathématiques. Cela n’est cependant pas anodin, car les calculs en virgule flottante, contrairement aux Serge Bays 32 Lycée Les Eucalyptus http://mathematice.fr calculs sur les réels, ne sont pas associatifs. Par exemple, dans un calcul en flottants IEEE double précision, (1050 + 1) − 1050 ne donne pas 1, mais 0. La raison est que 1050 + 1 est approximé par 1050 . Serge Bays 33 Lycée Les Eucalyptus Chapitre 5 Les fonctions Nous avons déjà rencontré des fonctions, par exemple la fonction print, la fonction abs, des fonctions du module math comme sqrt. Nous pouvons aussi définir et utiliser nos propres fonctions. 5.1 Définition d’une fonction En Python la définition d’une fonction est de la forme : def nomDeLaFonction(paramètres): corpsDeLaFonction def est un mot clé du langage qui dit à Python qu’une fonction va être définie, ce mot est suivi du nom de la fonction puis d’une parenthèse, entourant un ou plusieurs paramètres séparés par des virgules ou aucun paramètre, et enfin le caractère " :". Le corps de la fonction est un bloc de code indenté par rapport à la ligne de définition. Exemple : on étudie le mouvement vertical d’une balle lancée en l’air. Supposons que la position verticale de la balle à l’instant t est donnée par y(t) = v0 t − 21 gt2 , où v0 est la vitesse initiale et g l’accélération due à la gravité (environ 9,81). On peut définir une fonction nommée "position" par : def position(v,t): return v*t-0.5*9.81*t**2 On appelle la fonction, en précisant la valeur des paramètres, par : position(4,0.6) Lors de l’appel, on affecte les valeurs 4 et 0.6 respectivement aux paramètres v et t de la fonction (on dit aussi les arguments de la fonction) et le corps de la fonction est exécuté. Si le corps de la fonction contient l’instruction "return" alors l’appel de la fonction est une expression qui a donc une valeur. L’instruction suivante : print(position(4,0.6)) 34 http://mathematice.fr permettra d’afficher le résultat : 0.6341999999999999. S’il n’y a pas d’instruction "return" dans le corps de la fonction, alors l’appel de la fonction a la valeur "None". Ce type de fonction s’appelle une procédure. Par exemple : def successeur(n): n+=1 print(n) Il faut bien distinguer les arguments déclarés dans la définition de la fonction et les paramètres qui sont utilisés au moment de l’appel de la fonction. Les paramètres peuvent avoir des noms différents ou identiques à ceux des arguments ; cela n’a aucune importance, car les noms des arguments n’ont de signification qu’à l’intérieur de la définition de la fonction, et sont inconnus en dehors de la fonction. Le code suivant fonctionne parfaitement : def position(v,t): return v*t-0.5*9.81*t**2 vitesse=4 temps=0.6 position(vitesse,temps) 5.2 5.2.1 Espace et portée des variables Espace local Si dans la définition d’une fonction on fait référence à une variable x, Python vérifie dans l’espace local de la fonction si cette variable existe. Cet espace contient les paramètres qui sont passés à la fonction et les variables définies dans son corps. Si la variable x n’existe pas dans l’espace local de la fonction, Python va regarder dans l’espace local dans lequel la fonction a été appelée. Et là, s’il trouve bien la variable x il peut l’utiliser. 5.2.2 Portée d’une variable Quand Python trouve une instruction d’affectation, comme par exemple var = valeur1, il va changer la valeur de la variable "var" dans l’espace local de la fonction. Mais cet espace local est détruit après l’appel de la fonction. Donc une fonction ne peut pas modifier, par affectation, la valeur d’une variable extérieure à son espace local. Dans le code suivant, la variable x utilisée dans la fonction est distincte de la variable x définie au début du programme (x=3) et n’existe plus après l’appel de la fonction. Après l’instruction f(x), l’espace local de la fonction f est détruit. On pourrait d’ailleurs remplacer "x" par n’importe quel autre identificateur dans la définition de la fonction. x=3 def f(x): x+=2 print(x) f(x) # affiche 5 print(x) # affiche 3 Serge Bays 35 Lycée Les Eucalyptus http://mathematice.fr De même la fonction d’échange suivante ne produit pas le résultat espéré : a=2 b=5 def echange(x,y): x,y=y,x echange(a,b) print(a,b) #affiche 2 5 5.2.3 Variables globales Il existe un moyen de modifier, dans une fonction, des variables extérieures à celle-ci. On utilise pour cela des variables globales. Pour déclarer à Python, dans le corps d’une fonction, que la variable qui sera utilisée doit être considérée comme globale, on utilise le mot-clé global. Le code suivant permet de modifier la variable x extérieure à la fonction. x=3 def f(): global x x+=2 print(x) f() #affiche 5 print(x) #affiche 5 5.3 Algorithmes Plusieurs algorithmes concernant l’étude des fonctions sont étudiés au lycée. 5.3.1 Solutions d’une équation L’analyse mathématique, faite au préalable, permet grâce au théorème des valeurs intermédiaires d’affirmer : si f est continue croissante sur [a; b] et si k ∈ [f (a); f (b)], ou si f est continue décroissante sur [a; b] et si k ∈ [f (b); f (a)], alors l’équation f (x) = k admet une solution unique α dans [a; b] . Méthode par dichotomie Cette méthode permet de déterminer un encadrement de la solution α. A chaque étape l’amplitude de l’intervalle contenant la solution est divisé par deux. En dix étapes, on gagne environ trois décimales puisque 210 ' 1000. Exemple d’algorithme : Variables et Initialisation a et b, les bornes de l’intervalle [a ; b] f, la fonction (f change de signe entre a et b) m , milieu de l’intervalle "courant" Traitement Tant que b - a > 0.001 m prend la valeur (a+b)/2 Si f(m) et f(a) sont de même signe alors Serge Bays 36 Lycée Les Eucalyptus http://mathematice.fr a prend la valeur m sinon b prend la valeur m Sortie a et b On pourrait aussi utiliser une méthode par "balayage" ou une méthode par "calcul de valeurs aléatoires". Mais ces méthodes sont en général plus longues et sont plutôt utilisées pour la recherche d’extremum. 5.3.2 Recherche d’extremum Algorithme 1 : déterministe à pas constant Variables et Initialisation a, b les bornes de l’intervalle d’étude f, la fonction à étudier N, le nombre d’intervalles et dx le pas x, la valeur "courante" et y, la valeur de f(x) min prend la valeur f(a) max prend la valeur f(a) dx prend la valeur (b-a)/N x prend la valeur a Traitement Pour k de 1 à N x prend la valeur x+dx y prend la valeur f(x) Si y>max alors max prend la valeur y Si y<min alors min prend la valeur y Sortie Affiche min et max. Algorithme 2 : tabulation « aléatoire » d’une fonction Variables et Initialisation a, b les bornes de l’intervalle f la fonction à étudier N le nombre d’images à calculer min prend la valeur f(a) max prend la valeur f(a) Traitement Pour k variant de 1 a N x prend une valeur aléatoire entre a et b. Si f (x) > max alors max prend la valeur f(x) Si f (x) < min alors min prend la valeur f(x) Sortie Afficher min et max Serge Bays 37 Lycée Les Eucalyptus http://mathematice.fr 5.3.3 Test de la monotonie Attention, cet algorithme peut permettre de montrer que la fonction n’est pas monotone ou que la fonction peut être monotone mais dans ce cas il se peut qu’elle ait des variations de sens contraires sur certains intervalles très courts. Algorithme : Variables et Initialisation a, b les bornes de l’intervalle d’étude [a ; b] f, la fonction à étudier N, le nombre d’intervalles x, les valeurs successives de la variable dx prend la valeur (b-a)/N sens prend la valeur signe de la différence f(b) - f(a) x prend la valeur a Traitement Pour k variant de 1 à N Si ( f (x+dx)-f(x) n’est pas du même signe que sens ) alors Affiche "la fonction n’est pas monotone" Affiche x et x+dx Fin du programme x prend la valeur x+dx Affiche "La fonction semble monotone." Sortie Les affichages du traitement 5.3.4 Intégrales On peut encadrer une intégrale pour une fonction monotone positive ou déterminer une valeur approchée d’une intégrale. Encadrement Soit f une fonction continue, positive et croissante sur [a; b] et n un entier strictement positif. b−a . On partage [a; b] en n intervalles d’amplitude h = n Sur chaque intervalle [a + ih; a + (i + 1)h], où i varie de 0 à n − 1, on a l’encadrement f (a + ih) ≤ f (x) ≤ f (a + (i + 1)h) Z a+(i+1)h Donc hf (a + ih) ≤ f (x)dx ≤ hf (a + (i + 1)h), où à gauche et à droite, les produits a+ih sont des aires de rectangles. On obtient alors l’encadrement suivant : Z b n−1 n−1 X X h f (a + ih) ≤ f (x)dx ≤ h f (a + (i + 1)h) i=0 a i=0 On peut alors écrire un programme afin d’effectuer ce calcul. Serge Bays 38 Lycée Les Eucalyptus http://mathematice.fr Algorithme VARIABLES a, b, n, h DU TYPE NOMBRE integ_inf, integ_sup DU TYPE NOMBRE x DU TYPE NOMBRE DEBUT ALGORITHME Saisir a, b, n h PREND LA VALEUR (b-a)/n integ_inf PREND LA VALEUR 0 integ_sup PREND LA VALEUR 0 x PREND LA VALEUR a POUR i variant de 0 à n-1 integ_inf PREND LA VALEUR integ_inf+f(x) x PREND LA VALEUR x+h integ_sup PREND LA VALEUR integ_sup+f(x) FIN POUR integ_inf PREND LA VALEUR h*integ_inf integ_sup PREND LA VALEUR h*integ_sup AFFICHER integ_inf, integ_sup FIN ALGORITHME Si la fonction f est simplement continue sur [a; b], on peut obtenir une valeur approchée de l’intégrale en approximant f (x), sur chaque intervalle [a + ih; a + (i + 1)h], par f (a + (i + 1/2)h) : c’est la méthode des rectangles. Si on approche f (x), sur chaque intervalle [a+ih; a+(i+1)h], par [f (a + ih) + f (a + (i + 1)h)] /2, c’est la méthode des trapèzes. Serge Bays 39 Lycée Les Eucalyptus Chapitre 6 Les types composés 6.1 Types composés Définition : un objet de type composé est constitué de plusieurs objets ; nous étudions ici des suites d’objets qui sont indexés par des entiers indiquant leur position dans la suite. Nous avons déjà rencontré le type str pour les chaînes de caractères et nous allons étudier les types tuple et list. 6.1.1 Les n-uplets Les n-uplets, (couples, triplets, quadruplets, . . . ), sont des objets de type tuple, (uplet en français se dit tuple en anglais). Ces objets sont des suites ordonnées d’éléments, très semblables aux chaînes de caractères et avec des fonctions communes. Les éléments d’un n-uplet peuvent être de n’importe quel type et ne pas être tous du même type. Pour construire un objet de type tuple, on utilise des paranthèses et les éléments sont séparés par des virgules, par exemple : a=(3, 5.2) b=(’date’, 24, ’décembre’) c=(a,b) # couple formé d’un couple et d’un triplet Attention : pour un 1-uplet, contenant par exemple l’unique élément entier 3, on écrit (3,). Il ne faut surtout pas oublier la virgule. L’accès à une composante ou une suite de composantes se fait comme pour les chaînes de caractères : a[0] donne le nombre entier 3, b[2] donne le mot ’décembre’, b[0 :2] donne (’date’, 24). Comme pour les chaînes de caractères, on peut concaténer, avec l’opérateur +, un n-uplet avec un p-uplet pour obtenir un (n+p)-uplet : a+b est le quintuplet (3, 5.2, ’date’, 24, ’décembre’). On obtient la longueur d’un n-uplet avec la fonction len comme pour les chaînes de caractères : len(a+b) renvoie 5 et len(c) renvoie 2 (c’est un couple). Affectation multiple : l’affectation a = 3,5.2 crée aussi le couple (3, 5.2). Inversement, on peut déconstruire le couple par x,y =a ; on obtient x = 3 et y = 5.2. Ceci permet d’expliquer ce qui se passe lorsqu’on écrit x,y = y,x : cette instruction crée d’abord un couple t = (y, x), (expression à droite du signe =), puis déconstruit t en affectant à x le premier élément du couple et à y le deuxième. C’est pourquoi on obtient ainsi l’échange des variables x et y. Appartenance On teste l’appartenance à un n-uplet comme pour les chaînes de caractères avec l’opérateur in : 40 http://mathematice.fr >>> ’j’ in ’bonjour’ True >>> ’décembre’ in b True >>> ’e’ in b False Note Les objets de type tuple sont, comme les objets de type str, immuables : on ne peut pas changer un élément de l’objet par une affectation. L’instruction b[1]=25 renvoie une erreur. Ceci est intéressant dans certains cas, mais on peut aussi avoir besoin de pouvoir modifier la valeur d’un élément et il existe un type d’objet pour cela, c’est le type list. 6.1.2 Les listes Définition Une liste, objet de type list, est d’une certaine manière un n-uplet dont on peut changer les valeurs des éléments. C’est donc en particulier un ensemble ordonné d’éléments éventuellement hétérogènes. Une liste en Python correspond à ce que l’on appelle un tableau dans plusieurs autres langages. Syntaxe Les éléments d’une liste sont séparés par des virgules et entourés de crochets. Les opérateurs fonctionnent comme pour les n-uplets. Par exemple : liste1=[2,5,3,8] liste2=[’vert’,’rouge’] liste1[2]=’bleu’ #on change un élément de la liste print(liste1) #affiche [2, 5, ’bleu’, 8] liste3=liste1+liste2 # concaténation de deux listes print(liste3) #affiche [2, 5, ’bleu’, 8, ’vert’, ’rouge’] liste4=[liste1,liste2] #liste de listes print(liste4) #affiche [[2, 5, ’bleu’, 8], [’vert’, ’rouge’]] Attention, une liste composée d’un unique élément a s’écrit [a] sans virgule après le a contrairement à la règle pour les n-uplets. On peut utiliser la fonction range pour initialiser une liste d’entiers ou l’opérateur * pour la répétition : liste1=list(range(4)) # [0,1,2,3] liste2=list(range(3,6)) # [3,4,5] liste3=list(range(3,11,2)) # [3,5,7,9] liste4=3*liste2 # [3, 4, 5, 3, 4, 5, 3, 4, 5] Construction par compréhension C’est un procédé très utile de construction d’une liste. L’instruction est [expression(i) for i in range()], par exemple : Serge Bays 41 Lycée Les Eucalyptus http://mathematice.fr carre=[x**2 for x in range (1,10)] # construit [1,4,9,16,25,36,49,64,81] Une liste est "itérable". Ainsi si on a défini une fonction f qui s’applique au type des éléments d’une liste, on peut remplacer chaque élément de cette liste par son image avec le simple code suivant : for x in maliste: x=f(x) Insertion et extraction Pour ajouter un élément à la fin d’une liste, on utilise la méthode append : liste=[’vert’,’rouge’] liste.append(’jaune’) # on ajoute un élément à la fin de la liste print(liste) #affiche [’vert’,’rouge’,’jaune’] Pour insérer un objet dans une liste, on utilise la méthode insert ; pour extraire une sous-liste, on opère comme pour le type str : liste=[1,3,7,9] liste.insert(2, 5) # on insère à l’index 2 l’élément 5 print(liste) #affiche [1,3,5,7,9], 7 et 9 sont décalés vers la droite liste2=liste[1:4] print(liste2) #affiche [3,5,7] print(liste[-1]) #affiche 9, le dernier élément Suppression d’un élément et tri On utilise la méthode remove pour supprimer un élément et la méthode sort pour trier une liste : liste=[1,3,7,9,3,5] liste.remove(3) # on print(liste) #affiche liste.sort() #trie la print(liste) #affiche supprime l’élément 3 (seul le premier rencontré !) [1,7,9,3,5], 7, 9, 3 et 5 sont décalés vers la gauche liste [1,3,5,7,9] Référence Un objet de type list peut être modifié. C’est très pratique lorsque c’est voulu mais aussi dangereux par exemple avec le code suivant où on pourrait croire ne modifier que la liste "liste2" : liste1=[1,3,5,7,9] liste2=liste1 liste2.append(11) print(liste1) #affiche [1,3,5,7,9,11] Serge Bays 42 Lycée Les Eucalyptus http://mathematice.fr On peut penser que liste2 est une copie de liste1. Mais en fait liste1 et liste2 contiennent une référence vers le même objet. Si l’objet est modifié avec l’une des deux variables, la modification se voit aussi avec l’autre. Donc pour modifier une liste sans toucher à l’autre, il faut faire une véritable copie, c’est-à-dire créer un nouvel objet. Le code est le suivant : liste1=[1,3,5,7,9] liste2=list(liste1) # liste2 est un nouvel objet liste2.append(11) # seule liste2 est modifiée print(liste1) #affiche [1,3,5,7,9] 6.2 Les tableaux et les matrices Une liste peut être composée d’objets de différents types et en particulier de listes. Une liste composée de n listes de longueurs p représente un tableau ou une matrice (n, p) (n lignes et p colonnes). 6.2.1 Création On commence par créer une liste vide "matrice", puis avec une boucle for, on crée les listes "ligne" une par une en les ajoutant à la liste "matrice" : matrice=[] for n in range(6): # 6 lignes ligne=[ . . . ] # une ligne de longueur p matrice.append(ligne) 6.2.2 Utilisation La commande print(matrice[2]) permet par exemple d’afficher la troisième ligne. Puisque "matrice[2]" est une liste, la commande print(matrice[2][4]) permet par exemple d’afficher le cinquième élément de la troisième ligne. De manière générale la commande print(matrice[i][j]) permet d’afficher le (j+1)ème élément de la (i+1)ème ligne. Par exemple : matrice=[] for n in range(3): # 3 lignes ligne=[j*j for j in range(3*n,3*(n+1)] # 3 colonnes matrice.append(ligne) print(matrice) # affiche [[0, 1, 4], [9, 16, 25], [36, 49, 64]] print(matrice[1]) # affiche [9, 16, 25] print(matrice[1][2]) # affiche 25 6.2.3 Images Une image est un tableau (n lignes et p colonnes) de pixels (picture elements) et chaque pixel, dans le système RVB, est constitué de trois couleurs (rouge, vert, bleu). Dans un fichier au format bmp les pixels sont écrits les uns à la suite des autres ; chaque couleur est codée par un nombre de 0 à 255 codé par un octet et il faut donc trois octets pour coder un pixel. Afin d’écrire ou de lire un tel ficher on peut donc utiliser une liste de n listes de longueurs p si on s’intéresse à chaque pixel, ou bien une liste de n listes de longueur 3 × p si on s’intéresse à chacun des trois octets formant un pixel. Serge Bays 43 Lycée Les Eucalyptus http://mathematice.fr Attention : il peut y avoir un problème au niveau de la mémoire qui ne peut stocker des listes trop grandes. La taille maximale est de l’ordre de quelques centaines de millions. Il est possible de faire un test, par exemple avec l’instruction liste=[0]*n, pour déterminer la valeur maximale de n. Serge Bays 44 Lycée Les Eucalyptus Chapitre 7 Les fichiers 7.1 Gestion des fichiers Un programme s’exécute dans la mémoire volatile (RAM) de l’ordinateur. Pour conserver une trace, il faut utiliser une mémoire permanente comme un disque ou une clé où les données sont organisées en fichiers par le système d’exploitation. Si on travaille avec l’interpréteur, il est nécessaire de déterminer le répertoire de travail. Par défaut c’est le répertoire où est installé Python (par exemple "Python33"). On peut changer ce répertoire de travail avec la fonction chdir (change directory) du module os, par exemple : from os import chdir chdir(’C:/Users/Toto/Info/tp7’) Le répertoire "tp7" est maintenant le répertoire où seront créés les fichiers. Attention, ce répertoire doit préalablement exister. Si on travaille avec un fichier ".py", le répertoire courant est celui où est enregistré le fichier ".py". 7.1.1 Ouverture d’un fichier Il y a trois manières d’ouvrir un fichier en mode texte : on utilise la fonction open qui prend comme premier paramètre le nom du fichier et en second paramètre ’w’ pour le mode "écriture", ’r’ pour le mode "lecture" et ’a’ pour le mode "ajout". – En mode écriture, le fichier est créé dans le répertoire courant du programme ou écrasé s’il existe déjà puis ouvert en écriture. – En mode ajout le fichier doit déjà exister, il est ouvert en écriture et toutes les données écrites sont automatiquement ajoutées à la fin du fichier. – En mode lecture le fichier doit déjà exister et est ouvert en lecture. La syntaxe est : fic1=open(’fichier1’,’w’) fic2=open(’fichier2’,’r’) fic3=open(’fichier3’,’a’) 45 http://mathematice.fr 7.1.2 Fermeture d’un fichier Pour fermer un fichier, il y a une seule instruction : fic1.close() A la fin de l’utilisation, il est impératif de fermer le fichier, sinon son contenu ne peut pas être garanti. 7.2 Ecriture et lecture 7.2.1 Ecriture Par défaut, Python utilise les fichiers en mode texte et on y écrit des chaînes de caractères (type str) en utilisant la méthode write. Voici un premier exemple : fic=open(’fichier1.txt’,’w’) fic.write(’Bonjour, comment allez-vous ?’) fic.close() Ce code crée un fichier "fichier1.txt" dans le répertoire courant. Ce fichier contient sur une ligne la phrase : Bonjour, comment allez-vous ? Si ou souhaite écrire sur plusieurs lignes, on utilise le caractère ’\n’ pour un retour à la ligne. Le code est : fic=open(’fichier1.txt’,’w’) fic.write(’Bonjour,’+’\n’+ ’comment allez-vous ?’) fic.close() Le fichier contient alors les deux lignes : Bonjour, comment allez-vous ? Attention le code qui suit ne fonctionne pas pour écrire la phrase sur deux lignes : fic=open(’fichier1.txt’,’w’) fic.write(’Bonjour,’) fic.write(’comment allez-vous ?’) fic.close() On obtient la phrase sur une seule ligne sans espace après "Bonjour,". Si on relance le même programme, le fichier qui existe déjà va être écrasé et réécrit. Si on ne souhaite pas l’écraser et écrire à la suite dans ce fichier, on le rouvre avec l’instruction fic=open(’fichier1.txt’,’a’). Le code qui suit ajoute une nouvelle ligne à la fin du fichier texte : Serge Bays 46 Lycée Les Eucalyptus http://mathematice.fr fic=open(’fichier1.txt’,’a’) fic.write(’\n’+’Au revoir’) fic.close() Pour écrire des données numériques de type int ou float, il suffit de les convertir préalablement en type str. Par exemple si on souhaite écrire un tableau de valeurs pour une fonction, on convertit les nombres de type float en chaîne de caractères de type str et on utilise la méthode write : def f(x): return x*(1-x) fic=open(’fichier2.dat’,’w’) for i in range(101): x=i/100 y=f(x) fic.write(str(x)+’\t’+str(y)+’\n’) # ’\t’ pour une tabulation et ’\n’ pour un retour à la ligne fic.close() Ensuite on ouvre un logiciel comme Gnuplot où on écrit les instructions : set title "y=x(1-x)" plot ’C :\Users\Toto\Info\tp7\fichier1.dat’ w l Gnuplot lit alors le fichier texte de données et trace la courbe correspondante. 7.2.2 Lecture Pour lire dans un fichier texte, on ouvre le fichier en lecture et on utilise la méthode read. Il ne faut pas oublier de fermer le fichier après la lecture. (fic.read(n) lit n caractères, fic.read() lit tout le fichier). Serge Bays 47 Lycée Les Eucalyptus http://mathematice.fr fic=open(’fichier1.txt’,’r’) monfichier=fic.read() # monfichier est de type str print(monfichier) # ou print(fic.read()) lit et affiche tout le fichier fic.close() L’objet "fic" est un ensemble de lignes, chacune étant une chaîne de caractères. On peut récupérer une ligne dans une variable de type str avec le code suivant : fic=open(’fichier1.txt’,’r’) ligne=fic.readline() # lit la ligne courante et passe à la suivante fic.close() On peut aussi récupérer toutes les lignes dans une liste. Chaque élément de la liste est alors une chaîne de caractères. Chaque chaîne est une ligne du fichier. fic=open(’fichier1.txt’,’r’) lignes=fic.readlines() #liste de lignes print(len(lignes)) # affiche le nombre de lignes print(lignes[0]) # affiche la première ligne fic.close() On peut décomposer chaque ligne qui est une chaîne de caractères en mots. La méthode rstrip() supprime le caractère ’\n’ de retour à la ligne et la méthode split() découpe la ligne et la transforme en une liste de mots lorsque ces mots sont séparés par des espaces ou n’importe quel caractère. Pour ce code, on a créé un fichier ’lecture.txt’ qui contient la chaîne "bonjour tout le monde 25 53.4". fic=open(’lecture.txt’,’r’) chaine=fic.read() print(chaine.split()) fic.close() Ce code affiche : [’bonjour’, ’tout’, ’le’, ’monde’, ’25’, ’53.4’]. On a créé une liste de mots à partir d’une chaîne. Si les données à récupérer sont de type numérique, il suffit alors de convertir chaque mot en int ou en float. Avec le fichier de donnés "fichier2.dat’, créé plus haut, qui contient des lignes de deux flottants séparés par une tabulation (’\t’), le code est : fic=open(’fichier2.dat’,’r’) for ligne in fic: coord=ligne.rstrip().split(’\t’) # coord est une liste de 2 mots x,y=float(coord[0]),float(coord[1]) Serge Bays 48 Lycée Les Eucalyptus http://mathematice.fr 7.3 Fichiers binaires Un fichier est une suite de 0 et de 1 que l’on regroupe par octet. Par exemple l’octet qui vaut 41 en hexadécimal peut représenter le nombre entier 65 qui indiquera la quantité de rouge pour un pixel donné dans une image ou bien le code ASCII du caractère ’A’ s’il s’agit d’un fichier texte. C’est le format du fichier qui indique comment il va être interprété. Les méthodes write(n) et read(n) permettent d’écrire ou de lire n octets en mode binaire. Les objets lus ou écrits sont du type bytes (tableau d’octets). f = open("nom1", "rb") # b utilisé pour ouvrir le fichier en mode binaire g = open("nom2", "wb") octets=f.read(3) # lit 3 octets (octets est du type bytes) g.write(octets) # écrit les 3 octets # ou g.write(b’\x00\x41\x80\xff’) écrit 4 octets # ou g.write(b’\x00A\x80\xff’) écrit les 4 mêmes octets # ou g.write(bytes([0,65,128,255])) écrit les 4 mêmes octets f.close() g.close() Serge Bays 49 Lycée Les Eucalyptus Chapitre 8 Algorithmes : validité et complexité 8.1 Validité d’un algorithme itératif Un algorithme itératif est contruit avec des boucles, par opposition à récursif qui remplace les boucles par des appels à lui-même. 8.1.1 Invariants de boucle Le principe La méthode des "invariants de boucle" aide à prouver la validité d’un algorithme itératif. Définition Un invariant d’une boucle est une propriété qui est vérifiée à chaque exécution du corps de cette boucle. On utilise un raisonnement par récurrence pour prouver qu’une propriété est un invariant d’une boucle : • initialisation : on montre que la propriété est vérifiée avant le premier passage dans le corps de la boucle, • hérédité : on montre que si l’invariant est vérifié avant une exécution quelconque du corps de la boucle, il est encore vérifié avant l’exécution suivante. • conclusion : l’invariant est alors vérifié à la fin de la boucle, ce qui traduit le fait que la boucle réalise bien la tâche souhaitée. Exemple On effectue la division euclidienne de a par b où a et b sont deux entiers strictement positifs. Il s’agit donc de déterminer deux entiers q et r tels que a = bq +r avec 0 ≤ r < b. Voici un algorithme déterminant q et r : q=0 r=a tant que r ≥ b q =q+1 r =r−b fin du tant que On choisit comme invariant de boucle la propriété a = bq + r. – Initialisation : q est initialisé à 0 et r à a, donc la propriété a = bq + r = b.0 + a est vérifiée avant le premier passage dans la boucle. – Hérédité : avant une itération arbitraire, supposons que l’on ait a = bq + r et montrons que cette propriété est encore vraie après cette itération. Soient q 0 la valeur de q à la fin de l’itération et r0 la valeur de r à la fin de l’itération. Nous devons montrer que a = bq 0 + r0 . On a q 0 = q + 1 et r0 = r − b, alors bq 0 + r0 = b(q + 1) + (r − b) = bq + r = a. La propriété est bien conservée. 50 http://mathematice.fr 8.1.2 Terminaison Le principe L’algorithme étudié ci-dessus semble valide, mais rien ne prouve pour l’instant que le programme se termine, et que lorsqu’il s’arrête, on aura bien 0 ≤ r < b. Pour prouver qu’un programme s’arrête, on vérifie que la suite formée par les valeurs des variables au cours des itérations converge en un nombre fini d’étapes vers une valeur satisfaisant la condition d’arrêt. Exemple Nous reprenons l’exemple précédent. – Commençons par montrer que le programme s’arrête : la suite formée par les valeurs de r au cours des itérations est une suite d’entiers strictement décroissante : r étant initialisé à a, si a ≥ b alors la valeur de r sera strictement inférieure à celle de b en un maximum de a − b étapes. – Maintenant, si le programme s’arrête, c’est que la condition du "tant que" n’est plus satisfaite, donc que r < b. Il reste à montrer que r ≥ 0. Comme r est diminué de b à chaque itération, si r < 0, alors à l’itération précédente la valeur de r était r0 = r + b ; or r0 < b puisque r < 0. Et donc la boucle se serait arrêtée à l’itération précédente, ce qui est absurde ; on on déduit que r ≥ 0. En conclusion, le programme se termine avec 0 ≤ r < b et la propriété a = bq + r est vérifiée à chaque itération ; ceci prouve que l’algorithme effectue bien la division euclidienne de a par b. 8.1.3 Un autre exemple L’objectif est de calculer le produit de deux nombres entiers positifs a et b sans utiliser de multiplication : p=0 m=0 tant que m < a m=m+1 p=p+b fin du tant que Comme dans l’exemple précédent, le programme se termine car la suite des m est une suite d’entiers consécutifs strictement croissante, et atteint la valeur a en a étapes. Un invariant de boucle est ici : p = m.b – Intialisation : avant le premier passage dans la boucle, p = 0 et m = 0, donc p = m.b. – Hérédité : supposons que p = m.b avant une itération ; les valeurs de p et m après l’itération sont p0 = p + b et m0 = m + 1. Or p0 = (p + b) = m.b + b = (m + 1)b = m0 b. Donc la propriété reste vraie. – Conclusion : à la sortie de la boucle p = m.b. Puisqu’à la sortie de la boucle m = a, on a bien p = a.b. 8.2 Complexité Pour traiter un même problème, il existe souvent plusieurs algorithmes et un algorithme n’aura pas le même temps d’exécution s’il est programmé dans deux langages différents ou s’il est exécuté sur deux machines différentes. Pour pouvoir comparer deux algorithmes il faut donc trouver un moyen de mesurer le temps d’exécution indépendamment du langage et de la machine. En général, le temps d’exécution d’un algorithme sera évalué en fonction de la taille des données. Par exemple, le temps d’exécution d’une recherche dans un tableau dépend de la taille de ce tableau. Cette évaluation peut se révèler parfois très difficile, mais dans de nombreux cas, elle peut se faire en appliquant quelques règles simples. Serge Bays 51 Lycée Les Eucalyptus http://mathematice.fr 8.2.1 Mesure du temps d’exécution La quantité de données prise en entrée sera notée n ; par exemple la taille d’un tableau, le nombre de caractères d’une chaîne, etc. La procédure est la suivante : – nous comptons le nombre d’opérations un exécutées par l’algorithme sans nous intéresser au temps mis par une machine particulière à exécuter une opération. – nous étudions la croissance du nombre d’opérations un effectuées par l’algorithme en fonction du nombre n de données. Les règles pour évaluer le temps d’exécution sont alors les suivantes : • le temps d’exécution, relativement petit, d’une affectation, d’une comparaison ou d’une opération simple constitue l’unité de base. • Le temps pris pour effectuer une séquence "[p q]" est la somme des temps pris pour exécuter les instructions "p" puis "q". • le temps pris pour exécuter un test "if (b) : p else : q" est inférieur ou égal au maximum des temps pris pour exécuter les instructions "p" et "q", plus une unité pour évaluer l’expression "b". • le temps pris pour exécuter une boucle "for i variant de 1 à m : p" est m fois le temps pris pour exécuter l’instruction "p". Si le nombre m ne dépend pas des entrées de l’algorithme, alors le temps pris pour exécuter cette boucle est une constante. • le cas des boucles "while" est plus complexe à traiter puisqu’en général le nombre de répétitions n’est pas connu a priori. Pour une même quantité de données, le temps d’exécution peut être très variable : lorsqu’on recherche un élément dans un tableau, l’élément peut se trouver au début du tableau dans le cas le plus favorable ou à la fin du tableau dans le pire des cas. Le plus souvent nous considérerons le pire des cas. 8.2.2 Borne asymptotique supérieure Une suite u est de borne asymptotique supérieure v si pour n assez grand, u est majorée par v à une constante près. On écrira un ∈ O(vn ). Définition : un ∈ O(vn ) si ∃N ∈ N, ∃k ∈ R, ∀n > N , un ≤ kvn . En pratique, on utilise la propriété suivante : un Propriété : un ∈ O(vn ) si lim = ` (autrement dit, si n→+∞ vn 8.2.3 un vn ( u est majorée par k.v) converge vers une constante). Niveaux de complexité Complexité constante Un algorithme est en temps constant, O(1), si son temps d’exécution est indépendant du nombre de données ; par exemple, retourner le premier élément d’une liste ne dépend pas de la longueur de la liste. Complexité logarithmique Un algorithme de complexité logarithmique, O(ln n), croît de façon linéaire en fonction de 2n ; cela signifie que pour doubler le temps d’exécution, il faut mettre au carré le nombre de données. La recherche par dichotomie dans un tableau trié est de complexité O(ln n). Pour la plupart, ces algorithmes sont dans la pratique très performants. Serge Bays 52 Lycée Les Eucalyptus http://mathematice.fr Complexité linéaire Un algorithme est de complexité linéaire, O(n), si le temps de calcul croît de façon linéaire en fonction du nombre de données. La recherche séquentielle dans un tableau non trié est de complexité linéaire : dans le pire des cas, l’élément que l’on recherche est celui qui est examiné en dernier et il est donc nécessaire d’effectuer n itérations (qui s’effectuent en temps constant) pour examiner chaque élément du tableau. (Autres exemples : le calcul d’une somme ou d’une moyenne.) Complexité log-linéaire Certains algorithmes de tri sont de complexité O(n. ln n). C’est le meilleur résultat qu’il est possible d’obtenir avec des tris de comparaisons. Complexité quadratique D’autres algorithmes de tri, construits avec deux boucles imbriquées, effectuent un nombre d’itérations de l’ordre de n2 . Souvent, les problèmes pour lesquels il existe des algorithmes quadratiques, O(n2 ), admettent aussi des algorithmes de meilleure complexité. Complexité polynomiale Ce sont les algorithmes, en O(nk ), k fixé, dont le temps d’exécution peut être majoré par un polynôme. On utilise le terme polynomial par opposition à exponentiel. En algorithmique, il est très important de faire la différence entre les algorithmes polynomiaux, utilisables en pratique, et les algorithmes exponentiels, inutilisables en pratique. Complexité exponentielle Ces algorithmes en O(k n ), k > 1, sont tellement longs à l’exécution, qu’on ne les utilise presque jamais. Malheureusement, il existe des problèmes pour lesquels les seuls algorithmes de résolution exacte connus à l’heure actuelle sont de complexité exponentielle. 8.3 8.3.1 Exemples Recherche séquentielle dans un tableau non trié Le programme suivant effectue la recherche d’un élément dans un tableau t de taille n par balayage (en anglais, linear search). Le programme s’arrête dès que l’élément est trouvé ou lorsque le compteur i atteind la valeur n. Dans le pire des cas, on parcourt donc tout le tableau et la complexité est en O(n). Pour le tableau t, on peut utiliser les types list, tuple ou même le type str. def recherche(x, t): n = len(t) i = 0 while (i < n) and (x != t[i]): i = i + 1 if i < n: return i else: return False En utilisant une boucle "for", on peut écrire un programme plus condensé (ci-dessous) mais la complexité est toujours en O(n). Serge Bays 53 Lycée Les Eucalyptus http://mathematice.fr def search(x, t): for i in range(len(t)): if t[i]==x: return i return False 8.3.2 Recherche par dichotomie dans un tableau trié Si le tableau est trié on peut utiliser le principe de dichotomie (binary search en anglais). A chaque étape, on coupe le tableau en deux et on effectue un test pour savoir dans quelle partie peut se trouver l’élément cherché. C’est le principe diviser pour régner (en anglais divide-and-conquer). On utilise ici le type list pour pouvoir préalablement trier le tableau avec la méthode sort(). def rechercheDichotomie(x,liste): g = 0 d = len(liste) while g < d-1: k = (g+d) // 2 if x < liste[k]: d = k else: g = k if x == liste[g]: return g else: return False Après k itérations, si n est la taille du tableau, d − g ≤ n (démonstration par récurrence). 2k n ln n ≤ 1 dès que k ≥ et dans ce cas le programme s’arrête. La complexité de la recherche ln 2 2k dichotomique est donc O(ln n). Or 8.3.3 Recherche d’un mot dans une chaîne de caractères Le principe est le même que pour la recherche d’un caractère dans une chaîne mais ici il nécessaire d’ajouter une boucle "while" pour tester tous les caractères du mot. def searchWord(mot,texte): if len(mot)>len(texte): return False for i in range(1+len(texte)-len(mot)): j=0 while j<len(mot) and mot[j]==texte[i+j]: j+=1 if j==len(mot): return i return None Dans le pire des cas le programme va chercher le mot à toutes les places possibles et va tester tous les caractères du mot. La complexité est donc de l’ordre de m(1 + n − m) où m est la longueur du mot et n la longueur du texte. Serge Bays 54 Lycée Les Eucalyptus http://mathematice.fr En particulier le maximum est atteint pour m = n et on obtient alors une complexité de l’ordre de 2 n2 + 2n ; dans ce cas la complexité de l’algorithme est donc en O(n2 ). 4 Serge Bays 55 Lycée Les Eucalyptus Chapitre 9 Résolution d’une équation : méthodes de dichotomie et de Newton 9.1 Recherche dichotomique L’algorithme de recherche dichotomique ("bisection search" en anglais) a déjà été étudié. A partir de deux valeurs a et b encadrant une solution unique d’une équation f (x) = 0, on teste si la solution est plus grande ou plus petite que m = (a + b)/2 et suivant le résultat, on restreint la recherche à l’intervalle [a; m] ou à l’intervalle [m; b]. On reproduit ce schéma tant que l’amplitude de l’intervalle (qui est divisée par deux à chaque étape) est supérieure à une précision epsilon donnée. Algorithme : Variables et Initialisation a et b, les bornes de l’intervalle [a ; b] f, la fonction (f change de signe entre a et b) epsilon la précision Traitement Tant que b - a > epsilon m prend la valeur (a+b)/2 Si f(m) et f(a) sont de même signe alors a prend la valeur m sinon b prend la valeur m Sortie a et b (pour un encadrement) ou (a+b)/2 (valeur approchée) Une programmation possible en Python : def zeroDic(f,a,b,eps): while b-a > eps: m = (a+b)/2 if f(a) * f(m) > 0: a = m else: b = m return (a+b)/2 56 http://mathematice.fr L’amplitude de l’intervalle étant divisé par deux à chaque étape, on gagne un bit de précision à chaque passage dans la boucle "while". L’intérêt de cette méthode est que les conditions sur la fonction f ne sont pas trop exigeantes : être continue et changer de signe. Analyse de l’algorithme : Il est nécessaire de démontrer la terminaison et la validité de cet algorithme. b−a Pour la terminaison, il suffit de remarquer qu’après k étapes, b − a a été divisé par 2k et comme k 2 a pour limte 0 quand k tend vers l’infini, pour tout > 0, il existe une valeur de k à partir de laquelle toutes les amplitudes des intervalles seront inférieures à . Pour la validité, on utilise l’invariant f (a)f (b) ≤ 0. Cet invariant est bien vérifié avant l’entrée dans la boucle par hypothèse. Ensuite on suppose que cet invariant est vérifié avant un passage dans la boucle : si f (a) et f (m) sont de même signe, alors a prend la valeur de m et donc garde un signe contraire à celui de b ; si f (a) et f (m) sont de signe contraire, alors b prend la valeur de m et donc garde un signe contraire à celui de a. Ainsi les valeurs de a et b en sortie sont les bornes d’un intervalle d’amplitude maximale telles que f (a)f (b) ≤ 0. D’après le théorème des valeurs intermédiaires, la solution appartient à cet intervalle. Pour la complexité, si on ne tient pas compte de la complexité des calculs de f (m) lors des appels b−a b−a à la fonction, on remarque que la boucle est exécutée k fois si et seulement si ≤ < k−1 , soit k 2 ! 2 ! b−a b − a b − a b − a ≤ 2k < 2 . On obtient alors ln ≤ k ln 2 < ln 2 + ln ce qui nous donne ! ! b−a b−a ≤ k < 1 + log2 . Par exemple si b − a = 1 et = 2−p alors k = p. log2 9.2 Méthode de Newton 9.2.1 Principe On cherche la solution de l’équation f (x) = 0, c’est-à-dire l’abscisse du point d’intersection de la courbe C représentant f avec l’axe des abscisses. Sous certaines conditions sur f , on part d’une valeur x0 et on détermine l’abscisse x1 du point d’intersection de la tangente T1 à la courbe C au point d’abscisse x0 f (x0 ) avec l’axe des abscisses ; x1 est solution de l’équation : f 0 (x0 )(x−x0 )+f (x0 ) = 0. Donc x1 = x0 − 0 f (x0 ) et x1 est une valeur approchée de x. On recommence un certain nombre de fois avec xn et la tangente Tn f (xn−1 ) au point d’abscisse xn−1 . Soit xn = xn−1 − 0 ; la suite (xn ) converge vers la solution x. f (xn−1 ) 9.2.2 Exemples Calcul de l’inverse On détermine la solution x = 1 1 1 de l’équation − a = 0. On a f 0 (x) = − 2 et : a x x xn = xn−1 − Serge Bays 57 f (xn−1 ) f 0 (xn−1 ) Lycée Les Eucalyptus http://mathematice.fr 1 xn = xn−1 − xn = xn−1 + −a xn−1 −1 x2n−1 ! 1 − a x2n−1 xn−1 xn = xn−1 + xn−1 − ax2n−1 xn = xn−1 (2 − axn−1 ) Calcul de la racine carrée On détermine la solution de l’équation x2 − a = 0. On a f (x) = x2 − a et f 0 (x) = 2x. xn = xn−1 − f (xn−1 ) f 0 (xn−1 ) xn = xn−1 − x2n−1 − a 2xn−1 xn = x2n−1 + a 2xn−1 1 a xn = xn−1 + 2 xn−1 ! Programme en Python : def racine(a,x,eps): while abs(x*x-a) > eps: x=0.5*(x+a/x) return x On peut compléter le code précédent afin de compter le nombre d’itérations et comparer l’efficacité de cet algorithme avec celle de la recherche dichotomique. Avec la recherche dichotomique, pour une précision de 10−4 , si l’intervalle de départ a une amplitude de 1, il est nécessaire de le diviser en deux n fois avec 2n ≥ 104 , soit n ≥ 4 ln 10/ ln 2 ce qui nous donne n = 14. Avec la méthode de Newton, trois itérations sont suffisantes. Le nombre de décimales correctes est multiplié par deux à chaque étape. def racine(a,x,eps): cpt=0 while abs(x*x-a) > eps: x=0.5*(x+a/x) cpt+=1 return x,cpt Serge Bays 58 Lycée Les Eucalyptus http://mathematice.fr 9.2.3 Cas général f (xn−1 ) il est nécessaire de f 0 (xn−1 ) définir dans le programme la fonction f et la fonction f 0 , que l’on notera "dfdx". La variable "cpt" est un compteur permettant d’afficher le nombre d’itérations nécessaires pour obtenir la précision souhaitée. Mais nous ne connaissons pas à l’avance le nombre d’itérations et il y a des cas où la suite diverge, donc il est important de limiter ce nombre ; c’est le rôle de l’argument N dans le programme qui suit. Afin de calculer les termes de la suite (xn ) définis par xn = xn−1 − def newton(f,x,dfdx,eps,N=100): cpt=0 while abs(f(x)) > eps and cpt<=N: x=x-f(x)/dfdx(x) cpt+=1 return x, cpt, f(x) On peut améliorer ce code de plusieurs manières. – Dans la boucle, on évalue deux fois la quantité f (x). Sur de petits exemples cela n’a pas une grande importance, mais dans le cas de fonctions beaucoup plus compliquées, faire deux fois le même travail peut ne pas être négligeable. Nous pouvons donc stocker la valeur f (x) dans une variable locale. – Un problème sérieux est le risque de diviser par zéro ou par un nombre très petit qui pourrait créer une très grande valeur pour x et faire diverger la méthode. C’est pourquoi nous devons tester les valeurs de f 0 (x) et afficher un avertissement si une valeur devient très petite. – Il est aussi intéressant de stocker dans une liste les valeurs x et f (x) obtenues à chaque itération pour les imprimer ou les utiliser dans un graphique illustrant le comportement de la méthode de Newton. Pour cela nous pouvons ajouter en argument un booléen indiquant si nous stockons ou pas ces valeurs. Voici un code optimisé : def newton(f,x,dfdx,eps,N=100,save=False): valeur_f=f(x) cpt=0 if save: valeurs=[(x,valeur_f)] while abs(valeur_f) > eps and cpt<=N: valeur_dfdx=dfdx(x) if abs(valeur_dfdx)<1E-14: print("Attention, valeur de f’ trop petite") break x=x-valeur_f/valeur_dfdx cpt+=1 valeur_f=f(x) if save: valeurs.append((x,valeur_f)) if save: return x,valeurs else: return x, cpt, valeur_f Serge Bays 59 Lycée Les Eucalyptus http://mathematice.fr 9.3 Complément 9.3.1 Méthode de la sécante Le calcul de f 0 (x) peut être compliqué et si nous devons résoudre plusieurs équations, il peut être intéressant de faire effectuer ce calcul par le programme. Pour cela nous pouvons utiliser une approximation f (x + h) − f (x − h) avec h de l’ordre de 10−6 par exemple. en remplaçant f 0 (x) par 2h La méthode s’appelle alors la "méthode de la sécante". def DfDx(f,x): h=1e-6 return (f(x+h)-f(x-h))/(2*h) On remplace alors "dfdx" par "DfDx" dans le code de la fonction "newton" 9.3.2 Optimisation avec eval et exec Plutôt que modifier la fonction f dans le code du programme, on peut faire en sorte que le programme demande à l’utilisateur d’entrer l’expression de la fonction au clavier. On importe au préalable toutes les fonctions du module math (sin, cos, exp, . . . ). Puis on utilise les fonctions eval et exec. Le code suivant doit alors se trouver au début du programme. from math import * formule=input("entrer l’expression de la fonction") code=""" def f(x): return eval(formule) """ exec(code) D’une certaine manière, l’instruction exec(code) remplace la partie "code= """ . . . """ par les instructions se trouvant entre les guillemets. La fonction eval évalue le contenu de la chaîne "formule". (Par exemple eval(’2+3’) renvoie 5). 9.4 Utilisation de la bibliothèque scipy Le module optimize de la bibliothèque scientifique scipy contient les fonctions bisect et newton dans lesquelles sont programmées respectivement la méthode de dichotomie et la méthode de Newton. Les fonctions root et fsolve permettent également de trouver les valeurs approchées des zéros d’une fonction. import scipy.optimize def f(x): return x**2-2 a=1 b=2 Serge Bays 60 Lycée Les Eucalyptus http://mathematice.fr x=scipy.optimize.bisect(f,a,b) print("sol=",x) x=scipy.optimize.newton(f,a) print("sol=",x) x=scipy.optimize.fsolve(f,a) print("sol=",x) x=scipy.optimize.root(f,a) print("sol=",x) Serge Bays 61 Lycée Les Eucalyptus Chapitre 10 Résolution numérique d’équations différentielles : méthode d’Euler Les équations différentielles permettent de modéliser de nombreux phénomènes physiques. En général, on ne dispose pas de solutions analytiques : par exemple, l’équation θ00 = −k1 sin θ − k2 θ0 permet d’étudier le mouvement d’un pendule amorti et il donc est intéressant de pouvoir visualiser une approximation de la solution. 10.1 Méthode d’Euler S’il existe une unique solution y, sur un intervalle [a; b], de l’équation y 0 (x) = f (x, y(x)) avec y(a) fixé, il s’agit d’approcher y en un certain nombre de points répartis dans cet intervalle. En particulier, si n + 1 points sont répartis régulièrement sur [a; b], on définit le pas h = b−a n , soit xk = a + kh pour k = 0, 1, 2, . . . , n. L’objectif est de calculer des approximations yk des valeurs y(xk ). On utilise l’approximation y(x + h) − y(x) ' y 0 (x) appliquée pour chaque xk , et on obtient h y(xk+1 ) − y(xk ) ' hy 0 (xk ) = hf (xk , y(xk )) ' hf (xk , yk ) Schéma : on calcule les approximations pour k = 0, 1, 2, . . . , n − 1 par : xk+1 = xk + h et yk+1 = yk + hf (xk , yk ) On initialise avec y0 = y(x0 ) = y(a) (qui est la seule valeur exacte). Une programmation de ce schéma consiste à construire deux listes, une pour la suite (xk ) des abscisses et une pour la suite (yk ) des ordonnées. On définit une fonction euler(a,b,y0,h,f) qui prend en argument les valeurs extrêmes de l’intervalle a et b, la valeur initiale y(0), le pas h, la fonction f et renvoie les listes des abscisses et des ordonnées. def euler(a,b,y0,h,f): y = y0 x = a liste_y = [y0] # la liste des valeurs renvoyées liste_x = [a] while x+h <= b: y += h * f(x, y) liste_y.append(y) 62 http://mathematice.fr x += h liste_x.append(x) return liste_x, liste_y 10.2 Exemples 10.2.1 Exemple 1 On cherche une solution approchée de l’équation différentielle y 0 = −2x + 1, avec y(0) = 2, sur l’intervalle [0; 4]. La solution exacte est y(x) = −x2 + x + 2. Avec la méthode d’Euler, on calcule yk+1 = yk + hf (xk , yk ). On obtient la figure suivante avec un pas h = 0.5 : 10.2.2 Exemple 2 On cherche une solution approchée de l’équation y 0 = y, avec y(0) = 1, sur l’intervalle [0; 5]. La solution exacte est y(x) = ex . Avec la méthode d’Euler, on calcule yk+1 = yk +hyk , soit yk+1 = (1+h)yk . Avec y0 = 1, on obtient yk+1 = (1 + h)k+1 . La figure suivante est réalisée avec différentes valeurs du pas h. Avec la méthode d’Euler, l’erreur a deux causes constatées sur les exemples précédents : des erreurs d’arrondi dans les opérations effectuées par l’ordinateur et une erreur de discrétisation, (ek = yk − y(xk )), due au procédé de calcul. Il est important que l’erreur de discrétisation diminue lorsque le pas h diminue. On dit que la méthode converge si, pour tout k, yk tend vers y(xk ) quand h tend vers 0. Dans ce cas il faudra comme souvent faire un compromis entre la précision de l’approximation et le temps de calcul. Serge Bays 63 Lycée Les Eucalyptus http://mathematice.fr 10.2.3 Exemple 3 Problème de stabilité : on résout l’équation y 0 = −y avec y(0) = 1 sur l’intervalle [0; 30]. La solution exacte est y(x) = e−x . Ici l’intervalle est "grand" et si le pas n’est pas assez petit, on a un problème de stabilité. Instabilité pour h = 2, 5 : Stabilité pour h = 1, 5 : 10.3 Complément On peut résoudre une équation différentielle de degré 2 ou plus en vectorialisant l’équation. Serge Bays 64 Lycée Les Eucalyptus http://mathematice.fr L’équation y 00 + y = 0 est équivalente à (y, y 0 )0 = (y 0 , −y) = F (y, y 0 ), soit en posant Y = (y, y 0 ), on obtient l’équation Y 0 = F (Y ). La méthode d’Euler peut s’appliquer ici et pour la programmation, Y sera un objet de type tuple ou list. Mais les calculs se compliquent car par exemple : (3,4)+(2,5)=(3,4,2,5) (concaténation des deux couples) et non pas (5,9) comme on le souhaiterait. Il faudra donc en particulier détailler le calcul de Y + hF (Y ) dans la définition de la fonction euler. On commence par modifier la définition de la fonction f : def f(x,y): # y est un couple return (y[1],-y[0]) Puis la définition de la fonction euler : def euler(a,b,y0,h,f): x=a y=y0 liste_x=[a] liste_y=[y0] while x+h<=b: y=(y[0]+h*(f(x,y)[0]),y[1]+h*(f(x,y)[1])) # la difficulté liste_y.append(y) x+=h liste_x.append(x) return liste_x,liste_y Il est aussi possible et plus simple d’utiliser un objet de type array, (tableau en français). Un objet de type array ressemble à un objet de type list, mais ici, tous les éléments doivent être du même type et le nombre d’éléments doit être connu à la création. Les objets de type array se trouvent dans une bibliothèque appelée "Numerical Python" (NumPy) élaborée pour un calcul numérique optimisé. Il est alors plus simple de calculer avec des tableaux car les opérations mathématiques sont prédéfinies. Serge Bays 65 Lycée Les Eucalyptus http://mathematice.fr Exemples d’utilisation : import numpy as np a=np.array([3,4]) # convertit une liste en tableau b=np.array([2,5]) print(a+b) # affiche [5 9] print(3*a) # affiche [9 12] print((a+b)[0]) # affiche 5 Maintenant, il n’est plus nécessaire de modifier la définition de la fonction euler. def euler(a,b,y0,h,f): x=a y=y0 liste_x=[a] liste_y=[y0] while x+h<=b: y=y+h*f(x,y) # plus aucun problème de calcul liste_y.append(y) x+=h liste_x.append(x) return (liste_x,liste_y) Il faut cependant modifier la définition de la fonction f qui renvoie un objet de type array et l’appel de la fonction euler : def f(x,y): return array((y[1],-y[0])) # y est un array (F(a,b)=(b,-a)) x,y=euler(a,b,array((0,1)),eps,f) #initialisation y(0)=0 et y’(0)=1 Utilisation de la bibliothèque Scipy. Pour une équation du type x0 (t) = f (x(t), t), on utilise la fonction odeint de scipy.integrate. from math import cos, sin, pi import numpy as np import matplotlib.pyplot as plt import scipy.integrate as integ def f(x,t): return 2*(cos(t*x))*x*(1-x/2) t=np.linspace(0,15,num=300) sol=integ.odeint(f,4,t) plt.grid() plt.plot(t,sol) plt.show() Serge Bays 66 Lycée Les Eucalyptus http://mathematice.fr Exemple du pendule pesant amorti : def f(u,t): return [u[1],10*sin(u[0])-u[1]/4] t=np.linspace(0,10,num=200) sol=integ.odeint(f,[pi/2,0],t) plt.subplot(2,1,1) plt.grid() plt.plot(t,sol[:,0]) # angle fonction de t plt.subplot(2,1,2) plt.grid() plt.plot(sol[:,0],sol[:,1]) # diagramme de phase plt.show() Serge Bays 67 Lycée Les Eucalyptus Chapitre 11 Résolution d’un système linéaire inversible : méthode de Gauss 11.1 Matrices Nous pouvons utiliser des listes pour représenter des matrices. Une liste composée de n listes de longueurs p représente une matrice (n, p) (n lignes et p colonnes). 11.1.1 Création 2 2 −4 Par exemple la matrice 5 13 7 peut se définir en Python par le code suivant : 4 8 1 matrice=[[2,2,-4],[5,13,7],[4,8,1]] a=matrice[1][2] print(a) # affiche l’élément 7 On peut aussi créer une liste vide "matrice", puis créer les listes "ligne" une par une en les ajoutant à la liste "matrice" : matrice=[] for i in range(n): # n lignes ligne=[ . . . ] # une ligne de longueur p matrice.append(ligne) On pourrait envisager une autre possiblité en créant une liste composée de n listes de longueurs p où chaque élément est initialisé avec la valeur None ou la valeur 0. mat=2*[3*[None]] # initialisation de la matrice for i in range(2): for j in range(3): mat[i][j]=i+2*j print(mat[i]) print(mat) # par exemple 68 http://mathematice.fr Ce code affiche [0, 2, 4] [1, 3, 5] [[1, 3, 5], [1, 3, 5]] # la première ligne a été modifiée Donc cela ne fonctionne pas : la modification de la deuxième ligne s’est répercutée sur la première ligne. Un code qui fonctionne est : mat=2*[None] for i in range(2): mat[i]=3*[None] for i in range(2): for j in range(3): mat[i][j]=i+2*j qui construit la matrice souhaitée : [[0, 2, 4], [1, 3, 5]] Pour la suite, nous allons définir une fonction matrice qui crée, avec le code précédent, une matrice nulle (n, p) dont on pourra modifier les coefficients à volonté. def matrice(n,p): mat=n*[None] for i in range(n): mat[i]=p*[0] return mat ou bien, avec une construction en compréhension : def matrice(n,p): return [p*[0] for i in range(n)] 11.1.2 Opérations classiques Attention, pour une matrice (n, p), les lignes sont numérotées de 0 à n − 1 et les colonnes de 0 à p − 1. Somme de deux matrices Pour faire la somme de deux matrices (n, p), on utilise deux boucles "for" imbriquées. On peut alors définir une fonction somme ainsi : Serge Bays 69 Lycée Les Eucalyptus http://mathematice.fr def somme(m1,m2): n=len(m1) # on a besoin du nombre de lignes p=len(m1[0]) # et du nombre de colonnes mat=matrice(n,p) # une matrice nulle for i in range(n): # boucle sur les lignes for j in range (p): # boucle sur les colonnes mat[i][j]=m1[i][j]+m2[i][j] return mat Multiplication d’une matrice par un réel Le principe est le même que pour la somme : def multiple(m,k): n=len(m) p=len(m[0]) mat=matrice(n,p) for i in range(n): # boucle sur les lignes for j in range (p): # boucle sur les colonnes mat[i][j]=k*m[i][j] return mat Produit de deux matrices Pour le produit de deux matrices, c’est un peu plus compliqué et il faut vérifier que le nombre de colonnes de la première matrice est égal au nombre de lignes de la deuxième matrice. def produit(m1,m2): n=len(m1) p=len(m1[0]) q=len(m2) r=len(m2[0]) assert p==q mat=matrice(n,r) for i in range(n): # boucle sur les lignes for j in range (r): # boucle sur les colonnes for k in range(p): mat[i][j]+=m1[i][k]*m2[k][j] return mat 11.2 Autres opérations Pour appliquer l’algorithme du pivot de Gauss, il est nécessaire de définir de nouvelles opérations. On se placera dans le cas où le système a une solution unique. 11.2.1 Recherche du pivot A chaque étape, on recherche le plus grand pivot (en valeur absolue). Serge Bays 70 Lycée Les Eucalyptus http://mathematice.fr ligne 0 ligne 1 ligne 2 ligne s ligne s+1 ligne s+2 ... ligne n-1 2 2 −4 6 5 4 7 ... 2 13 7 3 −5 8 6 ... 4 1 −5 2 4 −3 ... 9 3 −5 2 8 ... 7 1 3 2 1 ... 9 5 5 3 2 ... 6 ... ... ... ... ... ... 4 −3 2 7 ... −4 Le pivot provisoire sur l’exemple est m[s][s] = 3, et on cherche le maximum en valeur absolue des nombres m[i][s] pour i variant de s+1 à n-1. Dans le cas où le système a une solution unique, on démontre que ces nombres ne sont pas tous nuls. La fonction pivot(m,s) prend en argument une matrice et le numéro du pivot que l’on cherche, (0 pour la première étape), et renvoie le numéro de la ligne contenant le pivot qui va être utilisé. def pivot(m,s): n=len(m) numpiv=s # numero du pivot provisoire for i in range(s+1,n): # boucle sur les lignes restantes if abs(m[i][s])>abs(m[numpiv][s]): numpiv=i return numpiv 11.2.2 Echange de lignes Pour échanger deux lignes d’une matrice, on commencera par faire une copie de la matrice afin de ne pas modifier la matrice d’origine du système. On définit donc une fonction copie qui renvoie une nouvelle matrice, copie de la matrice passée en argument : def copie(m): n=len(m) p=len(m[0]) mat=matrice(n,p) for i in range(n): # boucle sur les lignes mat[i]=m[i][:] return mat On définit maintenant une fonction change(m,i,j) qui prend en argument une matrice et les numéros des deux lignes à échanger : def change(m,i,j): n=len(m) p=len(m[0]) mat=copie(m) # on fait l’échange sur une copie de la matrice for k in range (p): # boucle sur les colonnes mat[i][k],mat[j][k]=mat[j][k],mat[i][k] return mat Serge Bays 71 Lycée Les Eucalyptus http://mathematice.fr 11.2.3 Transvection Les transvections sont les transformations centrales dans l’algorithme du pivot de Gauss. Si s est le numéro du pivot utilisé, on remplace chaque ligne m[i], pour i variant de s+1 à n-1, par ai,s Ls . m[i]- k*m[s], où k=m[i][s]/m[s][s], soit Li ←− Li − as,s Avec la notation matricielle habituelle, l’algorithme est le suivant : Pour i variant de s+1 à n-1 ai,s k= as,s Pour j variant de s à p-1 ai,j = ai,j − k × as,j def transvection(m,s): # s numero du pivot utilisé n=len(m) p=len(m[0]) mat=copie(m) for i in range(s+1,n): # boucle sur les lignes k=m[i][s]/m[s][s] for j in range (s,p): # boucle sur les colonnes mat[i][j]=mat[i][j]-k*mat[s][j] return mat 11.3 Algorithme du pivot de Gauss Le principe de l’algorithme du pivot de Gauss est d’exécuter des tâches répétitives qui fournissent à chaque étape un système équivalent dans le but d’obtenir finalement un système triangulaire. 2x + y − 3z = 4 −2y + 2z = 8 Voici un exemple de système triangulaire : 5z = 15 Algorithme de base : Pour s variant de 0 à n-2 Pour i variant de s+1 à n-1 ai,s k= as,s Pour j variant de s à p-1 ai,j = ai,j − k × as,j Avec la recherche du meilleur pivot : Serge Bays 72 Lycée Les Eucalyptus http://mathematice.fr Pour s variant de 0 à n-2 Recherche du pivot : piv=maxs≤i≤n−1 |ai,s | Si piv différent de s Echange des lignes s et piv Pour i variant de s+1 à n-1 ai,s k= as,s Pour j variant de s à p-1 ai,j = ai,j − k × as,j 11.3.1 Résolution d’un système triangulaire On résout un système triangulaire de bas en haut : on commence par la dernière équation puis à chaque étape, pour résoudre une équation, on substitue aux inconnues d’une ligne les valeurs trouvées dans les lignes inférieures. 2 1 −3 4 8 La matrice associée au système précédent est 0 −2 2 0 0 5 15 Nous allons définir une fonction solution(m) qui prend en argument une telle matrice et renvoie la solution du système associé. Si la solution est (x0 , x1 , . . . , xn−1 ), l’algorithme est le suivant : Pour i variant de n-1 à 0 p−2 X 1 ai,p−1 − xi = ai,j xj ai,i j=i+1 def solution(m): n=len(m) p=len(m[0]) sol=n*[None] # création d’une solution "vide" for i in range(n-1,-1,-1): # boucle sur les lignes sol[i]=m[i][p-1] for j in range(i+1,p-1): sol[i]-=m[i][j]*sol[j] sol[i]=sol[i]/m[i][i] return sol 11.3.2 Programme final Pour résoudre un système linéaire, il n’y a plus qu’à assembler les fonctions qui viennent d’être étudiées. On définit une fonction gauss(mat) qui prend en argument la matrice du système et renvoie la solution sous la forme d’une liste (que l’on peut considérer comme une matrice colonne). Serge Bays 73 Lycée Les Eucalyptus http://mathematice.fr def gauss(mat): n=len(mat) for s in range(n-1): # le dernier pivot est à l’avant dernière ligne piv=pivot(mat,s) if piv !=s: mat=change(mat,s,piv) mat=transvection(mat,s) sol=solution(mat) return sol Complexité La résolution finale du système nécessite n divisions et n(n − 1)/2 multiplications et soustractions. Pour s donné, une transvection nécessite n − 1 − s divisions pour le calcul de k, puis (n − 1 − s)(n − s + 1) multiplications et soustractions pour les nouvelles lignes ; s variant de 0 à n − 2, on obtient donc (n − 1)n/2 divisions, n−1 n−1 n−1 X X X et u(u + 2) = u2 + 2u = (n − 1)n(2n − 1)/6 + (n − 1)n multiplications et soustrac1 1 1 tions, donc au total n(n + 1)/2 divisions et n3 /3 + n2 − 4n/3 multiplications et soustractions. La complexité est en O(n3 ). 11.4 Utilisation de Numpy La bibliothèque Numpy contient un module linalg utile en algèbre linéaire. Par exemple pour résoudre un système MX=C : import numpy as np M=[[1,1,1],[1,0,-1],[-1,1,0]] C=[[6],[-2],[1]] X=np.linalg.solve(M,C) print(X) # solution : [1, 2, 3] M=[[2,2,-3],[-2,-1,-3],[6,4,4]] C=[[2],[-5],[16]] X=np.linalg.solve(M,C) print(X) # solution [-14, 21, 4] Serge Bays 74 Lycée Les Eucalyptus Chapitre 12 Bases de Données Relationnelles Comment gérer des données à l’aide de systèmes informatiques ? Supposons que des données sont stockées sur un serveur qui se trouve quelque part dans le monde. Un utilisateur a besoin d’accéder à ce serveur par un réseau afin par exemple, d’obtenir une information, de modifier des données, d’en supprimer ou d’en rajouter. L’utilisateur va écrire sa question ou requête dans un navigateur de manière simple (formulaire, mots-clés) et cette question sera transformée en un programme dont l’exécution permettra d’obtenir le résultat souhaité. Le système de fichiers sur un ordinateur ou un téléphone est un système élémentaire qui permet de gérer des données ; chaque fichier peut représenter un texte, une photo, une musique, un film, etc. On peut effectuer des recherches, ajouter ou supprimer des fichiers. Mais interroger ou modifier des bases de données qui peuvent être à l’echelle mondiale nécessite un système beaucoup plus complexe. Un système de gestion de bases de données doit permettre une plus grande rapidité d’accès aux données, un mode multi utilisateurs, assurer la sécurité, la confidentialité, l’intégrité, utiliser un langage "universel", etc. 12.1 Principes et architecture Un système de gestion de base de données facilite la manipulation des données par l’utilisateur qui ne doit pas se soucier de "comment cela fonctionne dans la machine" ; il est le médiateur entre la machine et la personne. Ce que voient les utilisateurs, l’organisation physique dans la machine et la logique de l’organisation des données (le modèle relationnel) sont indépendants. 12.1.1 Le concept de client-serveur L’utilisateur travaille sur une machine à l’aide d’une application ; c’est le client. La base de données est gérée par un serveur (une autre machine). Plusieurs utilisateurs (personnes ou programmes) peuvent effectuer des demandes au serveur simultanément. 12.1.2 Architecture trois-tiers Le plus souvent, l’utilisateur n’accède pas directement à la base de données. C’est l’application utilisée qui communique avec le serveur de base de données. Nous distinguons alors trois niveaux : le niveau utilisateur, le niveau applicatif et le niveau base de données. 12.2 Le modèle relationnel Prenons un exemple. Tous les élèves de première et deuxième année peuvent avoir pendant une semaine des cours de soutien en informatique tous les jours avec un professeur qu’ils choisissent. Pour organiser cela, on dresse donc un tableau qui ressemble à celui-ci : 75 http://mathematice.fr NumEleve 1 2 3 4 5 6 ... Nom Prenom Classe NomProf Java Céplus Java Java Python Céplus Numprof 1 2 1 1 3 2 Salle I403 I307 I403 I403 I508 I307 Trois professeurs, Mme Java, M. Céplus et M. Python, participent et chacun des trois travaille dans sa salle. Un tableau de ce type peut se rencontrer dans de nombreux domaines, par exemple lorsque des passagers réservent un vol Nice-Paris à une date donnée. Lorsque le nombre de lignes devient grand, la modification d’un tel tableau peut être longue et source de plusieurs erreurs. Par exemple : M. Python change de salle, M. Céplus sera absent et remplacé par M. Cémoins qui est dans une autre salle, les élèves qui avait choisi M. Céplus veulent changer pour Mme Java ... La séparation d’objets reliés permet une avancée importante : NumEleve 1 2 3 4 5 6 ... Nom Numprof 1 2 3 4 Nom Java Céplus Python Cémoins Prenom Classe Numprof 1 2 1 1 3 2 Salle I403 I307 I508 I215 Les modifications sont maintenant plus simples à gérer. Dans le modèle relationnel, les données sont organisées en tableaux à deux dimensions qui s’appelle relations 12.2.1 Les relations Une relation regroupe un ensemble de données homogènes concernant un même élément (ex : élèves, professeurs, . . . ). Les données sont organisées sous forme de colonnes (ex : Nom, Prenom, . . . ). Chaque colonne, appelée attribut, caractérise la relation. Les valeurs des attributs sont présentées sous forme de lignes, appelées Tuples ou n-uplets et chaque n-uplet est unique ; ces valeurs ont un type (entier, texte, flottant, ...) et appartiennent à un domaine (entier naturel inférieurs à 100, texte comportant au maximum 16 caractères, ...) Une relation est donc un sous-ensemble, caractérisé par un nom, du produit cartésien de domaines. Le produit cartésien d’un ensemble de domaine D1 , D2 , . . . , Dn noté D1 × D2 × . . . × Dn est l’ensemble des n-uplets (v1 , v2 , . . . , vn ) tels, pour tout i, vi ∈ Di . Serge Bays 76 Lycée Les Eucalyptus http://mathematice.fr 12.2.2 Notion de clé primaire Dans toute relation, un attribut, (ou un groupe d’attributs) permet d’identifier de manière unique les valeurs des autres attributs. On l’appelle une clé candidate et s’il y en a plusieurs, on en privilégie une, nommée la clé primaire. Certaines relations peuvent contenir un attribut qui est la clé primaire d’une autre relation et qui est alors appelée clé étrangère. Un schéma de relation se présente sous cette forme : Relation (attribut 1, attribut 2, . . . , attribut N) Clé primaire : attribut 1 Clé étrangère : attribut N en référence à attribut 1 de Relation X Par exemple : Eleves (Ideleve, Nom, Prenom, Adresse, CP, Ville, Tel, Classe) Clé primaire : Ideleve Clé étrangère : Classe en référence à une autre relation Un schéma de base de données est un ensemble de schémas de relations liés par des attributs communs. Si à une valeur d’un attribut A correspond une et une seule valeur d’un attribut B, on dit que l’attribut B est en dépendance fonctionnelle de l’attribut A. Par exemple : à un identifiant élève correspond un unique nom d’élève. 12.2.3 La normalisation relationnelle Dans l’élaboration des relations, la normalisation relationnelle permet d’éviter la redondance des données et faciliter leur mise à jour. La première forme normale Une relation est en première forme normale si tous ses attributs sont en dépendance fonctionnelle de la clé primaire et ne contiennent qu’une seule information. Ex : Eleves (Ideleve, Nom Prenom, Adresse, CP, Ville, Tel, Classe) Clé primaire : Ideleve Ici les attributs sont en dépendance fonctionnelle de la clé primaire mais l’attribut "Nom Prenom" contient deux informations, le nom et le prénom de l’élève ; il faut donc séparer cet attribut en deux attributs : Eleves (Ideleve, Nom, Prenom, Adresse, CP, Ville, Tel, Classe) La deuxième forme normale Lorsque la clé primaire est constituée de plusieurs attributs, on dit qu’une relation est en deuxième forme normale si elle est en première forme normale et si tous les attributs sont en dépendance fonctionnelle de l’intégralité de la clé primaire et pas seulement que d’une partie de celle-ci. La troisième forme normale Une relation est en troisième forme normale si elle est en deuxième forme normale et si tous les attributs sont en dépendance fonctionnelle directe de la clé primaire et uniquement de la clé primaire. Eleves (Ideleve, Nom, Prenom, Adresse, CP, Ville, Tel, Classe, Salle) Cette relation n’est pas en troisième forme normale car l’attribut Salle ne dépend pas de la clé primaire Ideleve, mais de l’attribut Classe. Serge Bays 77 Lycée Les Eucalyptus http://mathematice.fr 12.3 Algèbre relationnelle 12.3.1 Vocabulaire des bases de données Il est important d’avoir toujours en tête le vocabulaire utilisé en algèbre relationnelle et celui utilisé avec les bases de données. Relation = Table, Attribut = Champ = Colonne, Tuple = n-uplet = Ligne. 12.3.2 Opérateurs usuels sur les ensembles Ces opérateurs ne s’appliquent que sur des relation compatibles : des relations A(A1 , A2 , . . . , An ) et B(B1 , B2 , . . . , Bn ) qui ont le même nombre d’attributs et où pour tout i, les attributs Ai et Bi ont le même domaine. Union A ∪ B est la relation qui inclut tous les n-uplets appartenant à A ou à B (au sens mathématiques). Les doublons sont éliminés. intersection A ∩ B est la relation qui inclut tous les n-uplets appartenant à A et à B (au sens mathématiques). Différence A − B est la relation qui inclut tous les n-uplets appartenant à A mais pas à B. 12.3.3 Opérateurs spécifiques aux bases de données On travaille sur les relations : Eleves (Ideleve, Nom, Prenom, Adresse, CP, Ville, Tel, Numprof) Profs (Id, Nom, Prenom, Tel, Salle) Opérateur de projection Exemple : πNom, Prenom (Eleves) On ne retient que les n-uplets des attributs indiqués par l’opérateur, ici Nom et Prenom. Les doublons sont éliminés. Opérateur de sélection Exemple : σVille=’Nice’ (Eleves) On ne retient que les n-uplets vérifiant une propriété indiquée par l’opérateur, ici Ville=’Nice’. Opérateur de jointure Exemple : Eleves ./ Eleves.Numprof=Profs.Id Profs Ceci est quivalent à une sélection sur le produit cartésien : σEleves.Numprof=Profs.Id (Eleves × Profs) Le produit cartésien Eleves × Profs contient toutes les associations possibles entre une valeur de Eleves et une valeur de Profs. Serge Bays 78 Lycée Les Eucalyptus http://mathematice.fr Opérateur de renommage Exemple : ρAdresse←Rue (Eleves) Condition : les attributs Adresse et Rue ont le même domaine. On obtient alors le nouveau schéma : Eleves (Ideleve, Nom, Prenom, Rue, CP, Ville, Tel, Numprof) Division cartésienne Si S et R sont deux relations, la relation S ÷ R est la plus grande relation (pour l’inclusion) telle qu’il existe une relation R0 vérifiant ((S ÷ R) × R) ∪ R0 = S et ((S ÷ R) × R) ∩ R0 = ∅. En particulier, si S = R1 × R2 , alors S ÷ R2 = R1 . Serge Bays 79 Lycée Les Eucalyptus Chapitre 13 Le langage SQL Le SQL (Structured Query Language = langage de requêtes structuré) est un langage informatique de dialogue avec une base de données relationnelle. Une relation dans le modèle relationnel est une table dans le langage SQL. Une requête est une question posée à une base de données. Nous allons voir comment sont écrites les requêtes de base en SQL. 13.1 Les requêtes d’interrogation 13.1.1 La logique d’interrogation Procédure Champs (colonnes) à afficher Tables concernées Conditions de restriction à l’affichage Opération relationnelle Projection Sélection Jointure Ordre SQL SELECT FROM WHERE Le mot SELECT indique la liste des champs à afficher ; pour afficher tous les champs on utilise *. Le mot FROM indique à partir de quelles tables seront extraites les informations. Toute requête SQL se termine par un ; (point-virgule). Par convention, les instructions SQL sont écrites en majuscule dans le code d’un programme afin de les distinguer du langage de programmation. 13.1.2 Les opérations de base La projection La projection permet de n’afficher qu’une partie des attributs ou champs (colonnes) d’une table. Modèle relationnel : Eleves (Id, Nom, Prenom, Adresse, CP, Ville, Tel) Clé primaire : Id Requête 1 : Afficher toutes les informations concernant les élèves. Requête SQL : SELECT * FROM Eleves ; Requête 2 : Afficher les noms et prénoms des élèves. Algèbre relationnelle : πN om,P renom (Eleves) Requête SQL : SELECT Nom, Prenom FROM Eleves ; Requête 3 : Afficher les villes dans lesquelles habitent des élèves. Requête SQL : SELECT DISTINCT Ville FROM Eleves ; Le mot clé Distinct permet de supprimer les doublons. 80 http://mathematice.fr La sélection La sélection permet de n’afficher qu’une partie des lignes d’une table. On utilise le mot WHERE suivi du critère. La sélection peut être monocritère ou multicritère. On utilise des opérateurs de comparaison : = ou != peuvent être utilisés avec tout type de données ; >, <, >=, =< sont utilisables uniquement avec des données numériques ; On peut aussi utiliser LIKE (Comme), BETWEEN (Entre), AND (Et), OR (Ou), NOT. Pour des recherches sur des chaînes de caractères : % représente une chaîne de caractères quelconque ; _ représente un caractère quelconque. Champ au format texte ’ ... ’ Champ au format date ’mm/jj/aaaa’ Valeur de comparaison saisie par l’utilisateur [Texte à afficher] Modèle relationnel : Eleves (Id, Nom, Prenom, Adresse, CP, Ville, Tel) Clé primaire : Id Requête 1 : Afficher les nom et prénom des élèves qui habitent Nice. Algèbre relationnelle : πN om,P renom (σV ille=”N ice” (Eleves)) Requête SQL : SELECT Nom, Prenom FROM Eleves WHERE Ville= ’Nice’ ; Requête 2 : Afficher le nom et le numéro de téléphone des élèves qui habitent à Nice ou à Cannes. Requête SQL : SELECT Nom, Tel FROM Eleves WHERE Ville LIKE ’Nice’ OR Ville LIKE ’Cannes’ ; Requête 3 : Afficher le nom et le prénom des élèves dont le numéro de téléphone commence par 06 et dont la première lettre du nom est comprise entre A et M. Requête SQL : SELECT Nom, Prenom FROM Eleves WHERE Tel LIKE ’06%’ AND Nom BETWEEN ’A’ AND ’M’ ; La jointure La jointure permet de mettre en relation plusieurs tables, par l’intermédiaire des liens qui existent en particuler entre la clé primaire de l’une et la clé étrangère de l’autre. La jointure est une opération de sélection car elle permet de ne retenir que les enregistrements pour lesquels la valeur de la clé primaire d’une table correspond à la valeur de la clé étrangère d’une autre table. Modèle relationnel : Profs (Id, Nom, Prenom, Tel, Salle) Clé primaire : Id Eleves (Id, Nom, Prenom, Adresse, CP, Ville, Tel, Numprof) Clé primaire : Id, Clé étrangère : Numprof en référence à Id de Profs Requête : Afficher le nom des élèves et la salle où aura lieu le cours avec Monsieur Python. Algèbre relationnelle : πEleves.N om,P rof s.Salle σP rof s.N om=0 P ython0 (Profs ./Eleves.N umprof =P rof s.Id Eleves) Requête SQL : SELECT Eleves.Nom, Profs.Salle FROM Eleves JOIN Profs Serge Bays 81 Lycée Les Eucalyptus http://mathematice.fr ON Eleves.Numprof=Profs.Id WHERE Profs.Nom=’Python’ ; ou pour abréger : SELECT e.Nom, p.Salle FROM Eleves e JOIN Profs p ON e.Numprof=p.Id WHERE p.Nom=’Python’ ; Noter que c’est bien la même chose que l’extrait du produit cartésien Eleves × Profs : SELECT Eleves.Nom, Profs.Salle FROM Eleves, Profs WHERE Eleves.Numprof=Profs.Id AND Profs.Nom=’Python’ ; 13.1.3 Le champ calculé On peut afficher des données résultant d’une ou plusieurs autres données et éventuellement d’un calcul. Ces nouvelles données sont affichées dans un nouveau champ créé pour l’occasion. Modèle relationnel : Notes (Id, Maths, Physique, SI) Clé primaire : Id Requête 1 : Afficher l’identifiant des copies avec la note de Maths coefficientée par 5. Requête SQL : SELECT Notes.Id, Notes.Maths*5 AS Points_Maths FROM Notes ; 13.1.4 Les fonctions d’agrégation Ces fonctions permettent d’effectuer des opérations mathématiques ou des calculs statistiques sur un ensemble d’enregistrements sélectionnés. Compter les enregistrements On utilise la fonction COUNT. Le résultat de l’opération sera affiché dans un nouveau champ. Modèle relationnel : Notes (Id, Maths, Physique, SI) Requête 1 : Compter le nombre de copies dont la note de Physique est supérieur à 8. Requête SQL : SELECT COUNT (Notes.Id) AS Nombre_copies FROM Notes WHERE Notes.Physique > 8 ; Additionner les valeurs d’un champ numérique On utilise la fonction SUM comme la fonction COUNT avec la syntaxe SUM(). Calculer la moyenne des valeurs d’un champ numérique On utilise la fonction AVG (average=moyenne) comme la fonction COUNT avec la syntaxe AVG (). Afficher la valeur minimale d’un champ numérique On utilise la fonction MIN comme la fonction COUNT avec la syntaxe MIN (). Afficher la valeur maximale d’un champ numérique On utilise la fonction MAX comme la fonction COUNT avec la syntaxe MAX (). 13.1.5 Les clauses de regroupement Les clauses de regroupement permettent de réaliser des opérations sur des groupes d’enregistrements. Serge Bays 82 Lycée Les Eucalyptus http://mathematice.fr La clause GROUP BY La clause GROUP BY permet de créer des groupes d’enregistrements sur lesquels pourront être utilisées les fonctions d’agrégation. Elle est nécessaire dès lors que l’on souhaite afficher des données issues des tables et des données issues de fonctions d’agrégation. La syntaxe est : GROUP BY Champ1, Champ2, ... Requête SQL : SELECT ... COUNT (...) FROM ... GROUP BY ... ; La clause HAVING La clause HAVING permet d’appliquer des sélections sur les regroupements créés à l’aide de la clause GROUP BY. Contrairement à l’ordre WHERE qui sélectionne les enregistrements, la clause HAVING sélectionne les résultats d’une fonction d’agrégation. La syntaxe est : HAVING critères de sélection (par exemple ici " > 10 "). Requête SQL : SELECT ... COUNT (...) FROM ... GROUP BY ... HAVING COUNT (...) > 5 ; 13.2 Les requêtes de présentation des résultats 13.2.1 Renommage de colonne Requête SQL : SELECT moy AS ’moyenne informatique’ FROM ... WHERE ... ; 13.2.2 Ajout de texte Requête SQL : SELECT ’L’élève’ || nom, FROM ... ; 13.2.3 Le tri Le tri est une opération qui consiste à classer les enregistrements en fonction d’un ou plusieurs critères. La clause ORDER BY dispose de deux instructions de tri : ASC pour un tri dans l’ordre croissant et DESC pour un tri dans l’ordre décroissant. Modèle relationnel : Elèves (Id, Nom, Prenom, Adresse, CP, Ville, Tel, Numprof) Profs(Id, Nom, Prenom, Tel, Salle) Requête : afficher les noms des élèves regroupés suivant le nom de leurs professeurs. Requête SQL : SELECT Eleves.Nom, Profs.Nom FROM Eleves, Profs WHERE Eleves.Numprof=Profs.Id ORDER BY Eleves.Numprof ASC ; 13.3 Les requêtes de modification Il s’agit ici de modifier des données stockées dans les tables, c’est-à-dire des lignes ou n-uplets. 13.3.1 Les requêtes d’insertion de données Les requêtes d’insertion permettent d’ajouter un enregistrement dans une table. La syntaxe est : INSERT INTO table (champ1, champ2, ...) VALUES (’valeur1’, ’ valeur2’, ...) ; Modèle relationnel : Eleves (Id, Nom, Prenom, Adresse, CP, Ville, Tel, Numprof) Serge Bays 83 Lycée Les Eucalyptus http://mathematice.fr Requête SQL :INSERT INTO Eleves(Nom,Numprof) VALUES (’Toto’,’4’) ; 13.3.2 Les requêtes de mise à jour de données Les requêtes de mise à jour permettent de modifier les données stockées dans les tables. La syntaxe est : UPDATE table SET champs à modifier WHERE sélection ; Modèle relationnel : Eleves (Id, Nom, Prenom, Adresse, CP, Ville, Tel, Numprof) Requête : Modifier le numéro du professeur pour un élève donné. Requête SQL : UPDATE Eleves SET Eleves.Numprof=’4’ WHERE Eleves.Nom=’Dede’ ; 13.3.3 Les requêtes de suppression de données Les requêtes de suppression permettent de supprimer des données stockées dans les tables. La suppression concerne l’intégralité de l’enregistrement. La syntaxe est : DELETE FROM <nom table> WHERE <sélection> ; 13.4 Définition des données La création de tables ou de bases de données est complexe. Ceci sera étudié en TP avec l’importation et l’exportation de tables ou de bases. 13.4.1 Suppression d’une table Requête SQL : DROP TABLE <nom table> ; 13.4.2 Suppression d’un attribut Requête SQL : ALTER TABLE <nom table> DROP COLUMN <nom attribut> ; L’ajout et l’augmentation de la taille d’un attribut seront étudiés en TP. Serge Bays 84 Lycée Les Eucalyptus Chapitre 14 Les bibliothèques Numpy, Matplotlib, Scipy 14.1 Calculs avec des tableaux Si nous souhaitons établir un tableau de valeurs pour une fonction f , nous pouvons utiliser des objets de type list. Par exemple : def f(x): return x**2-3 n=11 # nombre de points en abscisse dx=2/(n-1) # espace entre les points sur [0;2] xliste=[i*dx for i in range(n)] yliste=[f(x) for x in xliste] Mais dans le cas, comme ici, où les éléments de chaque liste sont du même type et où la longueur de chaque liste est connue, il existe dans la bibliothèque Numpy un type plus approprié et avec lequel les calculs seront simplifiés : import numpy as np a=np.array(xliste) # a est du type numpy.ndarray La fonction np.array transforme une liste en tableau. Note : pour la suite nous utiliserons le mot "liste" pour un objet de type "list" et le mot "tableau" pour un objet de type "ndarray". Pour créer un tableau de longueur n rempli de zéros, nous écrivons : a=np.zeros(n) Les éléments de a sont du type float ; nous pouvons spécifier le type, par exemple int : a=np.zeros(n,int) Nous pouvons générer un tableau de la même longueur que a où les éléments sont du même type que ceux de a : 85 http://mathematice.fr b=np.zeros_like(a) Pour établir un tableau de valeurs d’une fonction, nous avons souvent besoin de générer un tableau de n nombres uniformément répartis sur un intervalle [p ; q] : x=np.linspace(p,q,n) Les éléments d’un tableau peuvent être extraits commme pour les listes, mais attention, si un élément de l’extrait est modifié, l’élément de l’original l’est aussi : x=np.linspace(0,2,11) # x=[ 0. 0.2 0.4 z=x[2:9:2] # z=[ 0.4 0.8 1.2 z[1]=5 # z=[ 0.4 5. 1.2 # x=[ 0. 0.2 0.4 0.6 0.8 1. 1.2 1.4 1.6 1.8 2. ] 1. 1.2 1.4 1.6 1.8 2. ] 1.6] 1.6] 0.6 5. Tout l’intérêt pour établir un tableau de valeur de la fonction f vue au début se voit ici : x=np.linspace(0,2,11) y=f(x) Il n’y a plus besoin d’utiliser des boucles for, la fonction f s’applique directement. Attention : les fonctions du module math ne peuvent pas s’appliquer sur les tableaux. Nous devons utiliser les versions de ces fonctions existant dans Numpy. x=np.linspace(0,2,11) y=np.cos(x)*np.exp(-x**2/2) 14.2 Tracé de courbes Nous allons utiliser la bibliothèque Numpy et le module Pyplot de la bibliothèque Matplotlib. Le code usuel pour commencer est : import numpy as np import matplotlib.pyplot as plt Le principe : on crée une liste d’abscisses et une liste d’ordonnées ; les points sont alors placés et reliés par des segments. Le code est le suivant : Serge Bays 86 Lycée Les Eucalyptus http://mathematice.fr x=[1,3,4,8] y=[2,1,5,3] plt.plot(x,y) plt.savefig(’figure’) #pour sauvegarder la figure plt.show() On change la couleur et l’épaisseur du trait, on ajoute un titre et des étiquettes : plt.plot(x,y,color=’red’,linewidth=6) plt.title(’Figure 1’) plt.xlabel(’abscisses’) plt.ylabel(’ordonnées’) On utilise Numpy pour les fonctions : Serge Bays 87 Lycée Les Eucalyptus http://mathematice.fr def f(x): return x**2*np.exp(-x**2) x=np.linspace(0,3,51) y=f(x) plt.plot(x,y,linewidth=4) plt.title(’Une courbe’) plt.xlabel(’x’) plt.ylabel(’y’) plt.legend([’t^2*exp(-t^2)’]) plt.axis([0,3,-0.05,0.6]) plt.show() Deux courbes sur la même figure avec la commande subplot(l,c,n) où l est le nombre de lignes, c le nombre de colonnes et n un compteur : def f(x): return x**2*np.exp(-x**2) def g(x): return x*np.exp(-x) x=np.linspace(0,3,51) plt.subplot(2,1,1) y=f(x) plt.plot(x,y,linewidth=4) plt.title(’Courbe 1’) plt.xlabel(’x’) plt.ylabel(’y’) plt.legend([’t^2*exp(-t^2)’]) plt.axis([0,3,-0.05,0.6]) plt.subplot(2,1,2) y=g(x) Serge Bays 88 Lycée Les Eucalyptus http://mathematice.fr plt.plot(x,y,linewidth=4) plt.title(’Courbe 2’) plt.xlabel(’x’) plt.ylabel(’y’) plt.legend([’t*exp(-t)’]) plt.axis([0,3,-0.05,0.6]) plt.show() 14.3 Algèbre linéaire Les bibliothèques Numpy et Scipy contiennent un module linalg utile en algèbre linéaire. Exemples avec Numpy : import numpy as np M=[[2,2,-3],[-2,-1,-3],[6,4,4]] C=[[2],[-5],[16]] #resolution de MX=C X=np.linalg.solve(M,C) # valeurs propres de M vp=np.linalg.eigvals(M) N=[[1,0,0],[0,3,2],[0,0,5]] # valeurs propres et vecteurs propres de N valp,vecp=np.linalg.eig(N) # determinant de N d=np.linalg.det(N) Serge Bays 89 Lycée Les Eucalyptus http://mathematice.fr # inverse de N invN=np.linalg.inv(N) 14.4 Calcul d’intégrales import numpy as np import scipy.integrate as integ def f(x): return x**2 lx=[i/10 for i in range(11)] ly=[f(x) for x in lx] # méthode des trapèzes s1=0 for i in range(10): s1+=(lx[i+1]-lx[i])*(ly[i]+ly[i+1])/2 # avec scipy.integrate s2=integ.trapz(ly,lx) #attention à l’ordre (ly,lx) #calcul de l’intégrale de f entre 0 et 1 s3=integ.quad(f,0,1) 14.5 Equation f(x)=0 Dichotomie et méthode de Newton avec scipy.optimize : import scipy.optimize def f(x): return x**2-2 a=1 b=2 x=scipy.optimize.bisect(f,a,b) print("sol=",x) x=scipy.optimize.newton(f,a) print("sol=",x) Les fonctions root et fsolve pour trouver des valeurs approchées des zéros d’une fonction : import scipy.optimize def f(x): return x**2-2 a=1 Serge Bays 90 Lycée Les Eucalyptus http://mathematice.fr x=scipy.optimize.fsolve(f,a) print("sol=",x) x=scipy.optimize.root(f,a) print("sol=",x) 14.6 Equations différentielles ordinaires Pour une équation du type x0 (t) = f (x(t), t), on utilise la fonction odeint de scipy.integrate. from math import sin, pi import numpy as np import matplotlib.pyplot as plt import scipy.integrate as integ def f(x,t): return 2*(cos(t*x))*x*(1-x/2) t=np.linspace(0,15,num=300) sol=integ.odeint(f,4,t) plt.grid() plt.plot(t,sol) plt.show() Le pendule pesant amorti : def f(u,t): return [u[1],10*sin(u[0])-u[1]/4] t=np.linspace(0,10,num=200) sol=integ.odeint(f,[pi/2,0],t) plt.subplot(2,1,1) plt.grid() plt.plot(t,sol[:,0]) # angle fonction de t plt.subplot(2,1,2) plt.grid() plt.plot(sol[:,0],sol[:,1]) #diagramme de phase plt.show() Pour plus d’onformations, consulter les documentations officielles : http://matplotlib.org/contents.html http://docs.scipy.org/doc/ Serge Bays 91 Lycée Les Eucalyptus