Série 4: L`indentation, les instructions de contrôle et coût
Transcription
Série 4: L`indentation, les instructions de contrôle et coût
Série 4: L’indentation, les instructions de contrôle et coût calcul Exercice 1 (niveau 1): Retrouver les erreurs Le programme suivant (cinq_erreurs.c) contient 6 erreurs. Trouvez les, et indiquez pour chaque erreur, s'il s'agit d'une erreur qui pourrait être détectée par le compilateur (erreur syntaxique). #include <stdlib.h> #include <stdio.h> int main (void) { int x; y; printf ("Please enter two numbers : ); scanf ("%d %d", &x, &y); if (x = y) printf ("Both numbers are equal!\n"); if ((x < 0) and (y < 0) printf ("Both numbers are negative!\n"); if (x > y) printf ("The second number is greater than the first!\n"); } return EXIT_SUCCESS; Exercice 2 (niveau 0): Les boucles (document séparé) Exercice 3 (niveau 0): A lire: L'indentation (document séparé) Exercice 4 (niveau 1): Creating / Reading / Manipulating / Writing Variable Size Multidimensional Arrays In this exercise, you will write a program to estimate scalar product of a matrix. Given a scalar λ and a matrix A the scalar product is given by λA. Your program shall start with asking the scalar first. After entering the scalar it shall ask two numbers, the height and the width of the matrix, respectively. Then, it shall ask for the content of the matrix, i.e the integers. The program will print out the result of the operation. Sample execution: Please enter the scalar value: 2 Please enter the height and the width of the matrix: 34 Please enter the content of the matrix: 1234 5678 9 10 11 12 The resulting matrix is: 2468 10 12 14 16 18 20 22 24 The matrix has to be represented by a Variable Length Array (VLA) with 2 indices. With this approach, the array declaration has to be made only after the value of the height (number of lines) and width (number of columns) is provided at execution time. Exercice 5 (niveau 1): Ce nombre est-il premier? Ecrivez un programme qui indique si un nombre entier entré au clavier par l'utilisateur est premier ou non. Voici l'algorithme en pseudo-code : Si le nombre à vérifier z est inférieur ou égal à 1 Sinon Fin_Si afficher "nombre non premier", fin Pour k commençant à 2, et tant que k2 <= z { Si z est divisible par k afficher "nombre non premier", fin Sinon k++; Fin_Si } afficher message "le nombre est premier" Exercice 6 (niveau 2): Crible d'Eratosthène Trouver et afficher tous les nombres premiers entre 2 et N (nombre lu au clavier) en utilisant la méthode du crible d'Eratosthène (source wikipédia). On utilisera un tableau de booléen à un seul indice. Exercice 7 (niveau 2 et 3): Boucles imbriquées: le triangle de Pascal Le triangle de Pascal est une table à deux indices (notées n et p), et dont la valeur d'une case est le coefficient binomial défini par Rappel: la factorielle n! est le produit des nombres entiers compris entre 1 et n. De plus, 0! = 1. Remarquez que, par définition la table. , il y a donc un 1 dans la diagonale de Type des variables, précision et dépassement de capacité: Malgré la définition de la factorielle qui suggère d'utiliser des entiers, on utilisera des variables de type double car leur précision avec une mantisse de 52 bits permet de représenter exactement tous les entiers consécutifs jusqu'à 253, ce qui est bien plus grand que le minuscule 231-1 offert par le type int. Au delà de cette limite de 253 de moins en moins d'entiers sont représentés exactement par le type double. On peut vérifier facilement avec un petit programme que la plus grande factorielle représentée exactement en double est 22!. Au delà la valeur de la factorielle ne dispose pas de la précision suffisante pour être représentée exactement. Votre programme doit donc tester n pour informer l'utilisateur du programme à propos du risque d'erreur introduit par le dépassement de 22!. De plus nous ne sommes pas à l'abri d'un dépassement de capacité pour des valeurs supérieures de n ; déterminez également quand ce dépassement se produit (quelle est la "valeur" produite pour la factorielle dans ce cas ?). a) affichez la table pour des valeurs de n données par l'utilisateur, p variant de 0 à n. Lorsque p > n, ne rien afficher. Par ailleurs, les nombres doivent être correctement alignés dans la table ; utilisez la possibilité d'indiquer une largeur dans un code de format ou des tabulations avec le caractère de contrôle \t. Exemple: style d'affichage désiré pour n valant 10 1 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10 1 3 6 10 15 21 28 36 45 1 4 1 10 5 1 20 15 6 1 35 35 21 7 56 70 56 28 84 126 126 120 210 252 1 8 1 84 36 9 1 210 120 45 10 1 b) complément sur le coût calcul du programme (Niveau 3): mesurez grossièrement le temps calcul pris par votre programme pour n = 1, 5, 10, 15, 20, 25, 30, etc.. en utilisant la structure de code ci-dessous. Reportez vos mesures sur un graphe avec n en abscisse et la durée (en nanoseconde) en ordonnée. Les mesures sont plus fiables s'il n'y a pas d'affichage (opération très lente qui fausse l'évaluation): Mesure de performances: La fonction clock() renvoie un int indiquant le temps CPU exprimé en unité d'horloge "clock_t" depuis le début de l'exécution du programme. Au-delà de INT_MAX la fonction clock() reprend à 0.Il n'y a pas de garantie que la fonction clock() renvoie 0 au début du programme, c'est pourquoi on travaille toujours sur la différence entre deux appels de cette fonction, un avant et un après la portion de code à tester. La durée correspondant à l'unité "clock_t" n'est pas standardisée. Pour convertir une durée de "clock_t" en seconde il faut la diviser par le symbole CLOCK_PER_SEC qui est définit dans <time.h>. Sur les machines COSUN, une unité "clock_t" correspond à 1 microseconde, ce qui est bien plus que le coût calcul du triangle de Pascal pour n=1. Donc on doit répéter le code dans une boucle qui l'exécute NFOIS de manière à avoir une durée mesurée >0 ; ensuite on divise par NFOIS pour obtenir la véritable durée du code testé. Pour information, si une unité de clock_t représente une microseconde, alors INT_MAX représente environ 72 minutes de temps CPU. Tout programme dont la durée d'exécution s'approche de cette valeur peut conduire à un mauvais calcul de la durée à cause de la remise à 0 de clock(). L'évaluation doit être répétée plusieurs fois pour disposer de chiffres fiables. #include<time.h> #define NFOIS 1000 // ajuster cette valeur jusqu'à obtenir // une durée mesurée >0 pour n=1 int k ; int start = clock() ; for(k=0 ; k < NFOIS ; k++) { ici le code SANS ENTREE-SORTIE dont on veut mesurer la durée d'exécution } int nb_cycles = clock()-start ; Il faut diviser nb_cycles par NFOIS et par le symbole CLOCKS_PER_SEC pour obtenir une durée en secondes. Cette valeur étant très petite pour la plupart des valeurs de n, il vaut mieux l'exprimer en nanoseconde en multipliant par 1e9. double duree_en_nano_sec = 1e9 *(double)nb_cycles / ( CLOCKS_PER_SEC * NFOIS) L’expression ci-dessus peut conduire à un overflow avec la multiplication apparaissant au dénominateur. Modifiez l’ordre des opérations sans changer le résultat de façon à éviter l’overflow. Cet exemple sera traité en cours pour déterminer son ordre de complexité, c'est à dire pour caractériser comment le coût calcul augmente lorsque la taille du problème augmente ; ici la taille du problème est déterminée par n . Apportez votre graphe de mesures au prochain cours. c) Refaites les questions a) et b), mais au lieu de calculer les coefficients binomiaux de façon indépendante, utilisez la relation de récurrence suivante: . Cette relation permet de calculer simplement et rapidement la valeur d'une case à partir de la valeur de la case précédente (sur une même ligne). Reportez les mesures en nanosecondes sur le même graphe que la question b) et constatez visuellement comment le coût calcul évolue en fonction de n.