TP Algorithmique Avancée numéro 1 bis

Documents pareils
1.6- Génération de nombres aléatoires

Le prototype de la fonction main()

Programmation système I Les entrées/sorties

Cours d initiation à la programmation en C++ Johann Cuenin

Bases de programmation. Cours 5. Structurer les données

Cours de C++ François Laroussinie. 2 novembre Dept. d Informatique, ENS de Cachan

Introduction à la programmation Travaux pratiques: séance d introduction INFO0201-1

Programmation système en C/C++

Cours d Algorithmique et de Langage C v 3.0

Projet L1, S2, 2015: Simulation de fourmis, Soutenance la semaine du 4 mai.

TP : Gestion d une image au format PGM

Arguments d un programme

Introduction au pricing d option en finance

Programmation C++ (débutant)/instructions for, while et do...while

INFO-F-105 Language de programmation I Séance VI

Travaux pratiques. Compression en codage de Huffman Organisation d un projet de programmation

Chap III : Les tableaux

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

Le langage C. Séance n 4


INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

Programmation C. Apprendre à développer des programmes simples dans le langage C

Licence Bio Informatique Année Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose...

Suivant les langages de programmation, modules plus avancés : modules imbriqués modules paramétrés par des modules (foncteurs)

Compression de Données - Algorithme de Huffman Document de Conception

I. Introduction aux fonctions : les fonctions standards

Programmation système de commandes en C

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

Programme Compte bancaire (code)

Introduction au langage C

INFO-F-404 : Techniques avancées de systèmes d exploitation

MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES. Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année

Génie Logiciel I. Cours VI - Typage statique / dynamique, fonctions virtuelles et classes abstraites, flots d entrées / sorties, et string

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

Premiers Pas en Programmation Objet : les Classes et les Objets

Algorithmique et Programmation, IMA

Programmation en C/C++

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy

et Programmation Objet

LES TYPES DE DONNÉES DU LANGAGE PASCAL

Approche Contract First

Claude Delannoy. 3 e édition C++

COMPARAISONDESLANGAGESC, C++, JAVA ET

Flux de données Lecture/Ecriture Fichiers

3IS - Système d'exploitation linux - Programmation système

Travaux Dirigés n 1 : chaînes de caractères

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

Introduction à l algorithmique et à la programmation M1102 CM n 3

Utilisation d objets : String et ArrayList

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Cours 1: Java et les objets

UE Programmation Impérative Licence 2ème Année

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

OS Réseaux et Programmation Système - C5

IN Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Chapitre 10. Les interfaces Comparable et Comparator 1

Algorithmique I. Algorithmique I p.1/??

TP, première séquence d exercices.

Cours de C. Petits secrets du C & programmation avancée. Sébastien Paumier

Une introduction à Java

Seance 2: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu.

TP 1. Prise en main du langage Python

Les structures. Chapitre 3

Introduction à l héritage en C++

Cours Programmation Système

Logiciel Libre Cours 3 Fondements: Génie Logiciel

Les arbres binaires de recherche

Introduction à la programmation concurrente

Initiation. àl algorithmique et à la programmation. en C

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

Les chaînes de caractères

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Entraînement au concours ACM-ICPC

Programmation Classique en langage C

Notions fondamentales du langage C# Version 1.0

Exceptions. 1 Entrées/sorties. Objectif. Manipuler les exceptions ;

TD3: tableaux avancées, première classe et chaînes

Introduction à MATLAB R

30.avr.10 Présentation miniprojet. 9.mars.10 Cours 3 4.mai.10 Cours C mars.10 Cours 4 11.mai.10 Cours C++ 2

Généralités sur le Langage Java et éléments syntaxiques.

Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Bases Java - Eclipse / Netbeans

Département Automatisation et Informatisation Année Programmation en C++ Institut des Sciences et Techniques de l Ingénieur d Angers

TP3 : Manipulation et implantation de systèmes de fichiers 1

Cours de C/C++ par la pratique. Hugues Talbot

Conventions d écriture et outils de mise au point

Le système de gestion des fichiers, les entrées/sorties.

Introduction à C++ et à wxwidgets

Poker. A rendre pour le 25 avril

03/04/2007. Tâche 1 Tâche 2 Tâche 3. Système Unix. Time sharing

Programmation stochastique

GESTION DES FICHIERS C/UNIX

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

Quelques éléments de compilation en C et makefiles

Architecture des Systèmes d Information Architecture des Systèmes d Information

Le Langage C Version 1.2 c 2002 Florence HENRY Observatoire de Paris Université de Versailles florence.henry@obspm.fr

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

Transcription:

TP Algorithmique Avancée numéro 1 bis Licence Informatique - 2 me année année 2017-2018 durée : 3 heures L objectif de ce TP est d étudier quelques aspects des langages C/C++, non directement liés aux structures de données, ni à l algorithmique avancée. Il est à réaliser de manière optionnelle, si vous terminez plus rapidement le TP précédent ou les TPs à venir. Préliminaires Créez, dans le dossier AAC, un sous-dossier nommé TP1bis, dans lequel vous développerez l ensemble des exercices de ce TP. 1 Génération du fichier d entiers Dans le TP précédent, des fichiers de 10000 et 100000 entiers vous étaient fournis, pour effectuer vos tests de performance. Dans cette première partie, vous allez développer un programme permettant de générer des fichiers de ce type. Génération d un nombre aléatoire Les fichiers à générer doivent contenir un certain nombre d entiers, qui seront générés aléatoirement. Pour ce faire, la bibliothèque cstdlib fournit une fonction nommée rand(), qui permet de retourner un nombre entier aléatoire compris entre 0 et RAND MAX. RAND MAX est le nom d une constante entière, également définie dans cstdlib, qui représente donc le plus gand entier qui peut être retourné par cette fonction 1. Pout obtenir un nombre compris entre 0 et une autre valeur, inférieure ou égale à RAND MAX, une manière assez simple d opérer est de calculer le reste de la division entière de la valeur retournée par rand(), par la valeur suivante de la valeur maximale souhaitée. Par exemple, si on souhaite obtenir un nombre aléatoire compris entre 0 et 99, il suffit d utiliser l opération rand()%100. Notez qu à chaque nouvel appel de rand(), un nouvel entier aléatoire est renvoyé. Question 1 Créer un fichier nommé genficint.cpp, y inclure les bibliothèques iostream et cstdlib et y définir la fonction main. Dans cette fonction, écrire le code permettant de générer et afficher 5 entiers aléatoires compris entre 0 et 100. Un dernier entier négatif devra être affiché, pour être compatible avec l application développée dans le TP précédent. Compiler votre application de telle sorte que l exécutable porte le nom genficint, puis tester celle-ci. Question 2 Votre application affiche ses résultats sur l écran. Or, on souhaite pouvoir ranger ceux-ci dans un fichier, qui pourra ensuite être utilisé par l application développée dans le TP précédent. Une manière simple de ranger les entiers générés dans un fichier, sans passer par du code spécifique, est d utiliser la forme inverse de la redirection vue dans le TP précédent. Ainsi, la commande :./genficint > monfichierdesortie.txt 1. La valeur de cette constante dépend de différents paramètres, tels que le compilateur utilisé ou la plateforme matérielle cible. À titre d exemple, sur une plateforme 64 bits et pour une version 4.8.4, sa valeur est de 2147483647. De manière générale, le standard du langage assure que la plus petite valeur possible de RAND MAX sera de 32767. 1

indiquera au système, via l opérateur de redirection >, que tout ce qui doit être affiché par l application ne doit pas être envoyé à l écran, mais vers le fichier dont le nom suit. Tester cet opérateur avec un nom de fichier à votre convenance. À noter que cet opérateur crée le fichier s il n existe pas, ou remplace son contenu par les données générées si le fichier existait déjà. Si vous souhaitez ajouter des données à un fichier existant, il faut utiliser l opérateur de redirection >>, qui permet de concaténer le contenu du fichier existant avec les nouvelles valeurs générées. Question 3 Vous avez dû noter qu à chaque fois que vous relanciez votre application, vous obteniez les mêmes 5 valeurs entières. Ceci est lié au fait que le générateur de nombres aléatoires interne au système est réinitialisé automatiquement de manière identique à chaque fois que l application démarre. De manière plus précise, ce générateur correspond à l évaluation de la formule mathématique d une suite v n, pour laquelle les nombres renvoyés par rand() démarrent systématiquement à l indice 0 : le premier appel à rand() retourne la valeur v 0, le second retourne la valeur v 1, et ainsi de suite. Pour pouvoir récupérer une suite d entiers différents à chaque nouvelle exécution, il est nécessaire d initialiser différemment le générateur de nombres aléatoires au lancement de l application. Une manière simple d effectuer cela est d utiliser la ligne d instruction suivante, au début de la fonction main : srand(time(null)); // initialisation du générateur de nombres aléatoires avec : srand(n) la fonction permettant d initialiser le générateur. Le paramètre n est un entier appelé graine, qui permet de changer l indice de la suite à partir duquel les nombres aléatoires sont calculés ; time(null) la fonction (définie dans ctime) qui, ici, va retourner le nombre de secondes écoulées depuis le 1er janver 1970. Modifier votre code en conséquence et vérifier que cela fonctionne. Modifier la fonction main de manière à ce qu elle demande d abord à l utilisateur le nombre de valeurs à générer et la valeur maximale autorisée. Lors du test de votre application, ne redirigez pas les résultats vers un fichier. 2 Passage de paramètre à la fonction main Relancez votre application, en redirigeant cette fois les résultats vers un fichier. Vous notez que votre application semble bloquée. Entrez un entier qui correspond au nombre de valeurs à générer, puis la valeur maximale autorisée. Vous notez que votre programme s arrête, mais qu il n a absolument rien affiché. Ceci est normal, puisque vous avez redirigé les affichages dans un fichier. Examinez à présent le contenu du fichier vers lequel vous avez redirigé les affichages : vous notez que les valeurs générées s y trouvent bien, mais qu elles sont précédées par les messages d interrogation à destination de l utilisateur. Lorsque l on redirige ce qui est affiché vers un fichier, tout y est envoyé, ce qui pose 2 problèmes ici : 1. l utilisateur ne voit plus les messages qui lui sont destinés ; 2. le fichier généré n est plus utilisable tel quel, puisqu il contient autre chose qu une liste d entiers. Pour pallier ce problème, nous allons voir comment passer des paramètres à un programme directement depuis la ligne de commande, de manière à ne pas avoir à interroger l utilisateur sur la valeur du nombre d entiers à générer et sur leur valeur maximale. Etape 1 Modifiez vos sources de manière à ajouter les paramètres suivants à la fonction main : int main(int argc, char*argv[]) avec : argc le nombre d arguments (argument count) passés sur la ligne de commande (y compris le nom de la commande) ; 2

argv un tableau de chaînes de caractères, chaque chaîne représentant l un des arguments de la ligne de commande (argument value). Ces paramètres sont les paramètres standards à utiliser lorsque l on souhaite passer des paramètres à la fonction main 2. Le principe de leur fonctionnement est illustré sur la figure 1 ci-dessous, sur un exemple mettant en jeu une application nommée essai. Figure 1 Passage de paramètres dans la fonction main. Lorsque l utilisateur entre la commande essai toto 123 3.14, le système lance l exécutable de l application nommée essai et lui passe 4 (argc) arguments dans le tableau argv. Le premier (case 0 du tableau) correspond toujours au nom de la commande lancée, et les suivants correspondent à chacun des mots et/ou valeurs fournis derrière le nom de la commande. Etape 2 Dans le programme, l utilisateur peut récupérer le nombre d arguments passé, qui sera toujours au moins égal à 1 (le nom de la commande) et les analyser pour déterminer ce qu il doit en faire. Modifiez votre fichier source de manière à : 1. commenter la partie dédiée à la saisie, au calcul et à l affichage des valeurs entières ; 2. ajouter les lignes suivantes : cout << "nb arguments = " << argc << endl; for(int i=0; i<argc; i++){ cout << i << " : " << argv[i] << endl; } Recompilez l application et testez là en passant différents paramètres sur la ligne de commande. Etape 3 Modifiez votre fichier source de telle sorte qu il récupère le nombre d entiers à générer et la valeur maximale autorisée sur la ligne de commande. On précise les points suivants : le contenu d une case du tableau argv peut être transformé en valeur entière (sous réserve de ne contenir que des chiffres), à l aide de la fonction atoi de la bibliothèque ctype. Par exemple :./genficint 10000 200 int i=atoi(argv[1]); => i vaut 10000 int j=atoi(argv[2]), => j vaut 200 si les deux paramètres correspondant aux valeurs à récupérer ne sont pas fournis, le programme devra s arrêter en affichant un message d erreur. Il ne vous reste plus qu à décommenter la partie générant et affichant les valeurs entières aléatoires et à rediriger l affichage vers un fichier pour stocker la liste de ces entiers. 3 Lecture dans un fichier Dans cette partie, vous allez étudier la manière de récupérer des informations dans un fichier texte 3 sans passer par les redirections, mais en accédant directement aux données. 2. Il est également possible de passer la liste des variables d environnement, mais cela ne sera pas vu ici. 3. Lire des données dans un fichier au format binaire est un peu différent et ne sera pas abordé ici. 3

Question 1 Créez un fichier nommé identite.cpp dans votre dossier TP1bis. Dans la fonction main, déclarez un vecteur de string. Ajoutez dans le fichier les deux fonctions suivantes : void lirenoms(vector<string> &v), dont l objectif est de lire une suite de noms de famille et de les stocker dans le vecteur passé en paramètre. Ces noms de famille seront lus, par redirection de l entrée standard, dans le fichier noms.txt fourni avec cet énoncé. On précise que le dernier nom stocké dans le fichier est Lejeune, afin que vous puissiez prévoir un test d arrêt pour la lecture ; void ecrirenoms(vector<string> v), dont l objectif est d afficher à l écran le contenu du vecteur passé en paramètre, qui est supposé contenir les noms de famille lus. Compilez et testez votre application. 3.1 Question 2 On souhaiterait à présent pouvoir également créer un vecteur contenant une liste de prénoms. Ces prénoms sont contenus dans le second fichier associé à cet énoncé, nommé prenoms.txt. Malheureusement, le mécanisme de redirection de l entrée standard ne peut pas accepter plusieurs fichiers différents. Il n est donc plus utilisable dans notre cas, pour lequel le contenu de deux fichiers doit être relu. Vous allez donc modifier votre application, en fonction des explications données ci-après, pour qu elle se passe de ce mécanisme et relise les données directement dans les fichiers. Explications Pour accéder au contenu d un fichier texte, plusieurs étapes successives sont nécessaires : 1. déclarer une variable de type ifstream (input file stream), qui va permettre d accéder au fichier. Ce type est déclaré dans la bibliothèque fstream ; 2. ouvrir le fichier. Cela se fait via la méthode open de la variable de type ifstream ; 3. lire dans le fichier. Comme pour cin que vous utilisez dejà (qui est une variable de type ifstream automatiquement initialisée au début de toute application), il suffit d utiliser l opérateur >> ; 4. refermer le fichier lorsque les lectures sont terminées, via la méthode close de la variable de type ifstream. Exemple Le code qui suit montre un exemple de ces différentes étapes, en relisant une chaîne de caractères dans un fichier. Notez les paramètres utilisés dans la méthode open, qui correspondent respectivement au nom du fichier à ouvrir et à son mode d ouverture (ici, en mode lecture). #include <iostream> #include <fstream> using namespace std; int main () { ifstream ifs; string s; // ouverture du fichier en mode lecture ifs.open ("test.txt", ifstream::in); // lecture d une cha^ıne dans le fichier ifs >> s; // affichage de la cha^ıne lue cout << s; // fermeture du fichier ifs.close(); } return 0; Application Modifiez le code de votre fonction lirenoms, de telle sorte qu elle relise les données dans le fichier noms.txt. Compilez et testez le fonctionnement de votre application. 4

Question 3 L un des inconvenients actuels de votre application est qu elle se doit de connaître le dernier nom du fichier pour pouvoir s arrêter. Il est possible de se passer de cette information, en utilisant la fonction eof() (end of file), utilisable depuis la variable de type ifstream utilisée pour la lecture. Cette méthode retourne true si la fin de fichier a été rencontrée lors d une tentative de lecture précédente. En utilisant cette méthode, modifiez votre fonction lirenoms de telle sorte que vous n ayez plus à tester la chaîne lue pour savoir si vous devez stopper la lecture. Un autre inconvénient de votre code actuel est que le nom du fichier à relire est codé de manière littérale dans votre source. Impossible donc de charger un autre fichier. Pour résoudre ce problème, vous allez modifier votre code de telle sorte que le prototype de la fonction lirenoms soit le suivant : void lirenoms(vector<string> &v, const char *filename); Le second paramètre (une chaîne de caractères) représentera le nom du fichier à relire. Modifiez en conséquence l appel à la méthode open. Vous modifierez ensuite votre fonction main de telle sorte qu elle accepte le nom du fichier à relire comme paramètre. La ligne de commande pour lancer votre application sera alors :./identite noms.txt On souhaite à présent que l application puisse également charger le fichier prenoms.txt et stocke tous les prénoms qu il contient dans un second vecteur. Le programme devra être lancé par la commande suivante :./identite noms.txt prenoms.txt Remarque Avant de démarrer le développement de cette fonctionnalité, vous vous poserez la question de savoir quelles sont les parties qu il est réellement nécessaire de coder... Question 5 Les deux fichiers à charger sont susceptibles de contenir la même valeur (nom ou prénom) en plusieurs exemplaires. On souhaite que les vecteurs qui sont remplis lors de la lecture ne comportent qu un seul exemplaire de chaque nom et prénom. 1. écrire le code de la fonction suivante, qui retourne true si la chaîne passée en premier paramètre est présente dans le vecteur passé en second paramètre ; bool estdejapresent(string s, vector<string> &v); 2. à l aide de cette fonction, modifiez votre fonction de lecture des données, de manière à assurer qu une donnée ne soit ajoutée que si elle n est pas déjà présente. 4 Génération d un fichier d identités On souhaite à présent, à partir des deux vecteurs de noms et prénoms chargés, générer un fichier comportant des identités aléatoires. On propose la structure suivante pour représenter une identité : typedef struct { string nom; string prenom; } identite; 5

Question 1 1. Après avoir ajouté cette structure à votre fichier source, déclarez un vecteur de type identité dans votre fonction main ; 2. Ecrivez une fonction permettant l affichage d un vecteur de type identite ; 3. Ecrivez le code de la fonction suivante : void genereridentite(vector<string> vn, vector<string> vp, vector<identite> &vi, int nbi); avec : vn un vecteur de noms ; vp un vecteur de prénoms ; vi le vecteur d identités à remplir ; nbi le nombre d identité à générer. Le nom et le prénom de chaque nouvelle identité seront choisis aléatoirement dans les vecteurs correspondants. 4. Modifiez votre fonction main pour appeler ces deux fonctions, et testez les en vous limitant pour le moment à un petit nombre d identités. Le fichier d identités peut alors être généré en redirigeant la sortie standard vers le fichier cible. Question 2 Modifiez votre fichier source pour que le nombre d identités à générer puisse être passé comme troisième paramètre sur la ligne de commande. Question 3 Le nombre de noms et de prénoms disponibles dans les fichiers fournis est limité. Ceci implique qu il est possible d avoir plusieurs fois la même identité lors de la génération aléatoire de celles-ci, la probabilité que cela survienne augmentant avec la croissance du nombre d identité à générer. Ecrivez la fonction suivante, qui renvoit true si une identité identique (même nom et même prénom) est déjà présente dans le vecteur d identité : bool identitedejapresente(identite id, vector<identite> vi); Utilisez ensuite cette fonction pour vous assurer qu il n y aura pas d identités identiques dans le vecteur généré. Remarque Le nombre de noms et prénoms uniques présents dans les deux fichiers texte permet d obtenir au maximum un peu plus de 23000 identités différents. Au-delà, vous ne parviendrez plus à générer une identité inexistante et votre programme bouclera indéfiniment. À noter que plus vous souhaiterez un nombre d identités proche de cette borne, et plus la probabilité que l identité générée existe augmentera, avec pour corollaire une augmentation importante du temps de calcul... Effectuez toutes les modifications nécessaires à l ajout d un âge (compris aléaoirement entre 7 et 77 ans) à une identité. Ce critère interviendra également lors de la comparaison de deux identités, sachant que deux personnes portant les mêmes noms et prénoms, mais ayant un âge différent, sont considérées comme différentes. Question 5 Ecrire l une des fonctions de tris vues dans le TP précédent, de manière à ce qu elle puisse trier des identités par ordre croissant, en considérant le nom de famille comme le critère principal, le prénom comme critère secondaire et enfin l âge comme dernier critère. 6