Exercices corrigés td3 algorithmique et programmation 2 gest
Télécharger PDFModule I132 : Algorithmique et Programmation 2 - Travaux Dirigés N° 3
Département d'Informatique – Parcours MIP (Semestre 3)
Date : 22 mars 2020 – Professeur : S. ANTER
Exercice 1 : Manipulation des Nombres Rationnels
Les nombres rationnels peuvent être définis en langage C par la structure suivante :
typedef struct {
int numerator; // Numérateur
int denominator; // Dénominateur
} rational;
Implémentez les fonctions suivantes pour manipuler cette structure :
-
Définir la fonction
int pgcd(int a, int b)permettant de calculer le Plus Grand Commun Diviseur (PGCD) de deux entiersaetb. Le PGCD est essentiel pour la simplification des fractions. -
Définir la fonction
rational creer(int n, int d)qui, étant donné deux entiersn(numérateur) etd(dénominateur), crée et retourne le rationnel correspondant àn/d. -
Définir une fonction
void afficher(rational r)permettant d'afficher le rationnelrsous la formen/d. Si le dénominateur est nul (d = 0), un message d'erreur sera affiché. -
Définir la fonction
rational simplifier(rational r)utilisant la fonctionpgcdpour retourner un nouveau rationnel simplifié, équivalent à celui passé en argument. Cette version ne modifie pas le rationnel original, elle en crée un nouveau. -
Définir une autre version de la fonction de simplification :
void simplifier_par_reference(rational *r). Cette fonction doit modifier le rationnel passé en argument en le remplaçant par sa forme simplifiée. Rappelez-vous qu'une fonction ne peut modifier ses paramètres que s'ils sont passés par adresse. -
Écrire une fonction
rational inverse(rational rat)qui renvoie l'inverse multiplicatif d'un rationnel (par exemple, l'inverse den/destd/n) passé en argument. L'implémentation doit échanger le numérateur et le dénominateur. -
Définir une fonction
int egale(rational r1, rational r2)qui renvoie 1 si les rationnelsr1etr2sont égaux (après simplification pour une comparaison correcte), et 0 sinon. -
Définir une fonction
rational somme(rational r1, rational r2)qui calcule la somme des deux rationnels donnés en argument. -
Écrire un programme principal (
main) pour tester toutes les fonctions implémentées.
Correction de l'Exercice 1
Voici les implémentations des fonctions demandées, accompagnées d'un exemple de programme principal.
#include <stdio.h>
// Définition de la structure pour un nombre rationnel
typedef struct {
int numerator; // Numérateur
int denominator; // Dénominateur
} rational;
// Fonction pour calculer le Plus Grand Commun Diviseur (PGCD)
// Utilise l'algorithme d'Euclide récursif.
int pgcd(int a, int b) {
if (b == 0) return a;
return pgcd(b, a % b);
}
// Fonction pour créer un nouveau rationnel
rational creer(int n, int d) {
rational r = {n, d};
return r;
}
// Fonction pour simplifier un rationnel et retourner une nouvelle instance
// La version de l'exercice est nommée simplifier0 dans le corrigé initial.
rational simplifier_par_valeur(rational r) {
rational a;
// Il est recommandé de travailler avec les valeurs absolues pour le PGCD
// et de gérer le signe du numérateur et du dénominateur séparément.
// Pour cet exercice, nous suivons l'implémentation donnée.
int common_divisor = pgcd(r.denominator, r.numerator);
a.denominator = r.denominator / common_divisor;
a.numerator = r.numerator / common_divisor;
return a;
}
// Fonction pour afficher un rationnel
void afficher(rational r) {
if (r.denominator != 0) {
printf("%d/%d\n", r.numerator, r.denominator);
} else {
printf("Erreur : Dénominateur nul\n");
}
}
// Fonction pour simplifier un rationnel en le modifiant par référence
void simplifier_par_reference(rational *r) {
int common_divisor = pgcd(r->denominator, r->numerator);
r->denominator /= common_divisor;
r->numerator /= common_divisor;
}
// Fonction pour calculer l'inverse multiplicatif d'un rationnel
// L'implémentation originale (nommée "oppose") correspond à l'inverse multiplicatif.
rational inverse(rational rat) {
rational a = {rat.denominator, rat.numerator}; // Inverse n/d devient d/n
return a;
}
// Fonction pour vérifier l'égalité de deux rationnels
int egale(rational r1, rational r2) {
// Simplification préalable des copies des rationnels pour comparer correctement
simplifier_par_reference(&r1);
simplifier_par_reference(&r2);
return r1.denominator == r2.denominator && r1.numerator == r2.numerator;
}
// Fonction pour calculer la somme de deux rationnels
rational somme(rational r1, rational r2) {
rational s;
s.numerator = r1.numerator * r2.denominator + r1.denominator * r2.numerator;
s.denominator = r1.denominator * r2.denominator;
// Il est souvent souhaitable de simplifier le résultat d'une somme.
s = simplifier_par_valeur(s); // Simplifie le résultat avant de le retourner.
return s;
}
// Programme principal de test
int main() {
rational r1, r2, r_somme;
printf("--- Test des fonctions pour les nombres rationnels ---\n");
// Test de creer et afficher
r1 = creer(12, 9);
printf("Rationnel initial r1 (creer(12, 9)) : ");
afficher(r1); // Affiche 12/9
// Test de simplifier_par_valeur
r2 = simplifier_par_valeur(r1);
printf("Rationnel r2 (simplifier_par_valeur(r1)) : ");
afficher(r2); // Affiche 4/3
// Test de simplifier_par_reference
printf("Simplification de r1 par référence...\n");
simplifier_par_reference(&r1);
printf("Rationnel r1 après simplification par référence : ");
afficher(r1); // Affiche 4/3
// Test de inverse
r2 = inverse(creer(3, 4));
printf("Rationnel r2 (inverse de 3/4) : ");
afficher(r2); // Affiche 4/3
// Test de egale
if (egale(creer(12, 9), creer(4, 3))) {
printf("Le rationnel 12/9 est égal à 4/3.\n");
} else {
printf("Le rationnel 12/9 n'est PAS égal à 4/3.\n");
}
if (egale(creer(1, 2), creer(2, 4))) {
printf("Le rationnel 1/2 est égal à 2/4.\n");
} else {
printf("Le rationnel 1/2 n'est PAS égal à 2/4.\n");
}
// Test de somme
r_somme = somme(creer(1, 2), creer(1, 3)); // (1*3 + 2*1) / (2*3) = 5/6
printf("La somme de 1/2 et 1/3 est : ");
afficher(r_somme); // Affiche 5/6
r_somme = somme(creer(1, 4), creer(1, 4)); // (1*4 + 4*1) / (4*4) = 8/16, simplifié en 1/2
printf("La somme de 1/4 et 1/4 est : ");
afficher(r_somme); // Affiche 1/2
return 0;
}
Exercice 2 : Manipulation de Fichiers et Caractères
-
Définir la fonction
int occurence(char* fichier, char c)permettant de compter le nombre d'occurrences du caractèrecdans le fichier texte dont le nom est passé en argument. Le fichier doit être créé au préalable avec du texte aléatoire pour les tests. -
Définir la fonction
char majuscule(char c). Sachant que la différence entre le code ASCII d'un caractère minuscule et son équivalent majuscule est de 32, cette fonction doit retourner le caractère majuscule correspondant au caractèrecpassé en argument, si ce dernier est une minuscule. Sinon, elle retourne le caractèreclui-même. -
Définir la fonction
void copie_majuscule(char* fichier_src, char* fichier_dest)permettant de copier le contenu d'un fichier source (fichier_src, supposé déjà créé) vers un fichier de destination (fichier_dest, à créer), en convertissant tous les caractères en majuscule lors de la copie. -
Donner un programme principal (
main) de test pour ces fonctions.
Correction de l'Exercice 2
Voici les implémentations des fonctions et un programme principal pour les tester.
#include <stdio.h>
#include <stdlib.h> // Pour exit()
// Fonction pour compter les occurrences d'un caractère dans un fichier
int occurence(char *fichier, char c) {
FILE *f = fopen(fichier, "r");
char current_char;
int count = 0;
if (f != NULL) { // Vérifie si le fichier a été ouvert avec succès
while ((current_char = fgetc(f)) != EOF) { // Lit caractère par caractère
if (current_char == c) {
count++;
}
}
fclose(f); // Toujours fermer le fichier après utilisation
} else {
fprintf(stderr, "Erreur d'ouverture du fichier %s pour lecture.\n", fichier);
exit(1); // Quitte le programme avec un code d'erreur
}
return count;
}
// Fonction pour convertir un caractère en majuscule si c'est une minuscule
char majuscule(char c) {
if (c >= 'a' && c <= 'z') {
return c - 32; // La différence ASCII entre majuscule et minuscule
}
return c; // Retourne le caractère tel quel s'il n'est pas une minuscule
}
// Fonction pour copier un fichier en convertissant son contenu en majuscules
void copie_majuscule(char *src, char *dest) {
FILE *fsrc, *fdest;
char c;
fsrc = fopen(src, "r"); // Ouvre le fichier source en lecture
fdest = fopen(dest, "w"); // Ouvre/crée le fichier destination en écriture
if (fsrc != NULL && fdest != NULL) {
while ((c = fgetc(fsrc)) != EOF) { // Lit un caractère du fichier source
fputc(majuscule(c), fdest); // Écrit le caractère converti dans le fichier destination
}
fclose(fsrc); // Ferme le fichier source
fclose(fdest); // Ferme le fichier destination
} else {
fprintf(stderr, "Erreur d'ouverture d'un des fichiers (source: %s, destination: %s).\n", src, dest);
exit(1); // Quitte le programme avec un code d'erreur
}
}
// Programme principal de test
int main() {
int n;
char char_to_find;
char *test_src_file = "fichier_source.txt";
char *test_dest_file = "fichier_destination_majuscule.txt";
// Création d'un fichier source de test
FILE *f_test_src = fopen(test_src_file, "w");
if (f_test_src) {
fprintf(f_test_src, "Ceci est un fichier de test avec des Caracteres minuscules et MAJUSCULES.\n");
fprintf(f_test_src, "Il contient quelques 'e' et des chiffres 123.\n");
fclose(f_test_src);
printf("Fichier '%s' créé pour les tests.\n\n", test_src_file);
} else {
fprintf(stderr, "Impossible de créer '%s'. Certains tests peuvent échouer.\n\n", test_src_file);
return 1; // Termine avec erreur si le fichier de test ne peut être créé
}
printf("--- Test des fonctions de fichier et caractère ---\n");
printf("Donnez un caractère à rechercher dans '%s' : ", test_src_file);
// L'espace avant %c est important pour ignorer les caractères blancs résiduels (ex: newline)
scanf(" %c", &char_to_find);
n = occurence(test_src_file, char_to_find);
printf("Le caractère '%c' apparaît %d fois dans '%s'.\n", char_to_find, n, test_src_file);
printf("Majuscule de 'a' est '%c'.\n", majuscule('a'));
printf("Majuscule de 'Z' est '%c'.\n", majuscule('Z'));
printf("Majuscule de '5' est '%c'.\n", majuscule('5'));
printf("Majuscule du caractère '%c' entré est '%c'.\n", char_to_find, majuscule(char_to_find));
copie_majuscule(test_src_file, test_dest_file);
printf("\nLe contenu de '%s' a été copié en majuscules dans '%s'.\n", test_src_file, test_dest_file);
printf("Vérifiez le contenu de '%s' pour confirmer la conversion.\n", test_dest_file);
// Supprimer les fichiers de test après exécution (optionnel)
remove(test_src_file);
remove(test_dest_file);
printf("\nFichiers de test supprimés.\n");
return 0;
}
Foire Aux Questions (FAQ)
-
Q : Pourquoi est-il important de simplifier les nombres rationnels ?
R : Simplifier les nombres rationnels permet de les représenter de la manière la plus concise (par exemple, 2/4 est simplifié en 1/2), ce qui facilite les comparaisons (comme dans la fonction
egale) et assure des calculs plus précis en évitant des numérateurs et dénominateurs inutilement grands. Cela aide également à maintenir une forme canonique pour chaque rationnel. -
Q : Quelle est la différence entre le passage de paramètre par valeur et par adresse en C ?
R : Le passage par valeur (
rational r) transmet une copie de la variable à la fonction. Toute modification effectuée sur ce paramètre à l'intérieur de la fonction n'affectera pas la variable originale. Le passage par adresse (rational *r) transmet l'emplacement mémoire de la variable, permettant à la fonction de modifier directement l'originale. C'est essentiel pour des fonctions commesimplifier_par_referencequi doivent altérer l'objet appelant. -
Q : Comment gérer les erreurs d'ouverture de fichier en C ?
R : Il est crucial de toujours vérifier la valeur de retour de la fonction
fopen(). Sifopen()retourneNULL, cela signifie que le fichier n'a pas pu être ouvert (il n'existe peut-être pas, ou les permissions sont insuffisantes). Dans ce cas, un message d'erreur doit être affiché (souvent versstderr) et le programme devrait gérer cette situation de manière appropriée, par exemple en se terminant avec un code d'erreur (exit(1)) ou en retournant un indicateur d'échec.