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.