Exercice langage C: Programmes calcule date et jours

ExerciceC-id2824


Exercice 1

Si (jma) n'est pas le dernier jour d'un mois alors son lendemain est (j+1, ma), sinon c'est (1, m+1, a) sauf si m = 12, auquel cas le lendemain est (1, 1, a+1). Nous devons donc écrire la condition qui caractérise que (j,ma) est le dernier jour d'un mois, ce qui équivaut, puisque la date est garantie correcte, à :

  • j = 31, ou bien
  • j = 30 et le mois est avril, juin, septembre ou novembre, ou bien
  • j = 29 et le mois est février, ou bien
  • j = 28, le mois est février et l'année n'est pas bissextile.

D'autre part, une année est bissextile si elle est divisible par 4 mais pas par 100. Cependant, les années multiples de 400 sont bissextiles. D'où le programme suivant, dans lequel on reconnaît sans peine la traduction des quatre cas ci-dessus.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include 
#include 

int j, m, a, i;

main() {
   printf("j m a ?\n");
    scanf("%d%d%d", &j, &m, &a);

    if (j == 31 
            || (j == 30 && (m == 4 || m == 6 || m == 9 || m == 11))
            || (j == 29 && m == 2)
            || (j == 28 && m == 2 && ! (a % 4 == 0 && a % 100 != 0 || a % 400 == 0))) {
        j = 1;
        if (m == 12) {
            m = 1;
            a = a + 1;
        }
        else
            m = m + 1;
    }
    else
        j = j + 1;

   printf("%d %d %d\n", j, m, a);
}

 

Exercice 2

Tout le travail à faire ici c'est programmer la formule donnée (nous l'acceptons sans explication!) sans y faire le moindre regroupement ou simplification car les barres de fraction indiquent des quotients entiers, donc impliquent des troncations qui sont nécessaires à la formule (par exemple, à cause de ces troncations, -2ns+ns/4 n'est pas toujours égal à -7ns/4).

Voici le programme, pour une fois il n'y a pas tellement de possibilités :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include 
#include 

int j, m, a, as, ns, f;

main() {
   printf("j m a ? ");
    scanf("%d%d%d", &j, &m, &a);
   printf("le %d/%02d/%d est un ", j, m, a);

    if (m >= 3)
        m = m - 2;
    else {
        m = m + 10;
        a = a - 1;
    }
    ns = a / 100;
    as = a % 100;
    f = (j + as + as / 4 - 2 * ns + ns / 4 + (26 * m - 2) / 10) % 7;

    if (f < 0)
        f = f + 7;

    switch (f) {
        case 0:
           printf("dimanche\n");
            break;
        case 1:
           printf("lundi\n");
            break;
        case 2:
           printf("mardi\n");
            break;
        case 3:
           printf("mercredi\n");
            break;
        case 4:
           printf("jeudi\n");
            break;
        case 5:
           printf("vendredi\n");
            break;
        default:
           printf("samedi\n");
    }
}

 



Après avoir décalé le mois et l'année comme indiqué (puisque ma et m1a1 ne coexistent pas, il n'y a pas besoin de deux paires de variables distinctes), on sépare le numéro de l'année en deux morceaux : a = 2003 donnens = 20 et as = 3 puis on calcule f.

Dans de nombreux cas f est négatif. Par exemple, pour le 1er mars 2002, f = -30. Cela pose un problème car là où un arithméticien dirait que -30 % 7 vaut 5 (en effet, -30 = -5 x 7 + 5) la bibliothèque C dira que -30 % 7 vaut -2 (car -30 = -4 x 7 - 2). Il se trouve que 5 et -2 sont deux réponses justes mais -2 nous gêne pour la suite du programme, d'où l'ajustement opéré [la bibliothèque garantit que xn est compris, quel que soit x, entre - (n - 1) et (n - 1)].

Enfin, il n'y a plus qu'à traduire le résultat obtenu, un nombre compris entre 0 et 6, et un texte "lundi", "mardi", etc. La solution proposée est la plus simple. Une solution au mieux équivalente, au pire un poil moins efficace, consiste à remplacer switch par une cascade de if :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  ...
    if (f == 0) 
       printf("dimanche\n");
    else if (f == 1) 
       printf("lundi\n");
    else if (f == 2) 
       printf("mardi\n");
    else if (f == 3) 
       printf("mercredi\n");
    else if (f == 4) 
       printf("jeudi\n");
    else if (f == 5) 
       printf("vendredi\n");
    else 
       printf("samedi\n");
}

 

Si vous connaissez les tableaux de chaînes de caractères (c'est assez pointu, on y reviendra plus tard) voici une solution plus élégante :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include 
#include 

int j, m, a, ns, f;

char *jour[] = { "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" };

main() {
   printf("j m a ? ");
    scanf("%d%d%d", &j, &m, &a);
   printf("le %d/%02d/%d est un ", j, m, a);

    if (m >= 3)
        m = m - 2;
    else {
        m = m + 10;
        a -= 1;
    }
    ns = a / 100;
    a = a % 100;
    f = (j + a + a / 4 - 2 * ns + ns / 4 + (26 * m - 2) / 10) % 7;   

    if (f < 0)
        f = f + 7;
   printf("%s\n", jour[f]);
}