Aller au menu - Aller au contenu

Icône TP : Calculs en boucle

Mise à jour : 02/02/2012
Difficulté : Facile Facile Creative Commons BY-NC-SA
22 955 visites depuis 7 jours, dont 258 sur ce chapitre classé 15/786
Ça y est, grâce au chapitre précédent, vous devez avoir une bonne idée de ce que sont les boucles. Par contre, vous ne voyez peut-être pas encore complètement qu'elles vont vous servir tout le temps.
C'est un élément qu'il est primordial de maîtriser. Il vous faut donc vous entrainer pour être bien sûr d’avoir tout compris.

C’est justement l’objectif de ce deuxième TP. Finie la théorie des boucles, place à la pratique en boucle !
Notre but est de réaliser des calculs qui vont avoir besoin des for et des foreach.

Vous êtes prêts ? Alors, c'est parti :)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Instructions pour réaliser le TP

Le but de ce TP va être de créer 3 méthodes.

La première va servir à calculer la sommes d'entiers consécutifs. Si par exemple je veux calculer la somme des entiers de 1 à 10, c'est à dire 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, je vais appeler cette méthode en lui passant en paramètres 1 et 10, c'est-à-dire les bornes des entiers dont il faut faire la somme.

Quelque chose du genre :

Code : C#
1
2
Console.WriteLine(CalculSommeEntiers(1, 10));
Console.WriteLine(CalculSommeEntiers(1, 100));

Sachant que le premier résultat de cet exemple vaut 55 (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55) et le deuxième 5050.

La deuxième méthode acceptera une liste de double en paramètres et devra renvoyer la moyenne des doubles de la liste. Par exemple :

Code : C#
1
2
List<double> liste = new List<double> { 1.0, 5.5, 9.9, 2.8, 9.6};
Console.WriteLine(CalculMoyenne(liste));

Le résultat de cet exemple vaut 5.76.

Enfin, la dernière méthode devra dans un premier temps construire une liste d’entiers de 1 à 100 qui sont des multiples de 3 (3, 6, 9, 12, …). Dans un second temps, construire une autre liste d’entiers de 1 à 100 qui sont des multiples de 5 (5, 10, 15, 20, …). Et dans un dernier temps, il faudra calculer la somme des entiers qui sont communs aux deux listes … vous devez bien sur trouver 315 comme résultat :)

Voilà, c’est à vous de jouer …

Bon, allez, je vais quand même vous donner quelques conseils pour démarrer. Vous n’êtes pas obligé de les lire si vous vous sentez capables de réaliser cet exercice tout seul.

Vous l’aurez évidemment compris, il va falloir utiliser des boucles.
Je ne donnerai pas de conseils pour la première méthode, c’est juste pour vous échauffer :) .

Pour la deuxième méthode, vous allez avoir besoin de diviser la somme de tous les doubles par la taille de la liste. Vous ne savez sans doute pas comment obtenir cette taille. Le principe est le même que pour la taille d’un tableau et vous l’aurez sans doute trouvé si vous fouillez un peu dans les méthodes de la liste. Toujours est-il que pour obtenir la taille d’une liste, on va utiliser liste.Count, avec par exemple :

Code : C#
1
int taille = liste.Count;


Enfin, pour la dernière méthode, vous allez devoir trouver tous les multiples de 3 et de 5. Le plus simple, à mon avis, pour calculer tous les multiples de 3, est de faire une boucle qui démarre à 3 et d’avancer de 3 en 3 jusqu’à la valeur souhaitée. Et pareil pour les multiples de 5.

Ensuite, il sera nécessaire de faire deux boucles imbriquées afin de déterminer les intersections. C’est-à-dire parcourir la liste de multiple de 3 et à l’intérieur de cette boucle, parcourir la liste des multiples de 5. On compare les deux éléments, s’ils sont égaux, c’est qu’ils sont communs aux deux listes.

Voilà, vous devriez avoir tous les éléments en main pour réussir ce TP, c'est à vous ^^

Correction

J’imagine que vous avez réussi ce TP, non pas sans difficultés, mais à force de tâtonnements, vous avez sans doute réussi. Bravo.
Si vous n’avez même pas essayé, pas bravo :pirate: .
Quoi qu’il en soit, voici la correction que je propose.
  • Première méthode :

Code : C#
1
2
3
4
5
6
7
8
9
static int CalculSommeEntiers(int borneMin, int borneMax)
{
    int resulat = 0;
    for (int i = borneMin; i <= borneMax ; i++)
    {
        resulat += i;
    }
    return resulat;
}


Ce n’était pas très compliqué, il faut dans un premier temps construire une méthode qui renvoie un entier et qui accepte deux entiers en paramètres.
Ensuite, on boucle grâce à l’instruction for de la borne inférieure à la borne supérieure (incluse, donc il faut utiliser l’opérateur de comparaison <= ) en incrémentant un compteur de 1 à chaque itération.
Nous ajoutons la valeur du compteur à un résultat, que nous retournons en fin de boucle.
  • Seconde méthode :

Code : C#
1
2
3
4
5
6
7
8
9
static double CalculMoyenne(List<double> liste)
{
    double somme = 0;
    foreach (double valeur in liste)
    {
        somme += valeur;
    }
    return somme / liste.Count;
}



Ici, le principe est grosso modo le même, la différence est que la méthode retourne un double et accepte une liste de double en paramètres. Ici, nous utilisons la boucle foreach pour parcourir tous les éléments que nous ajoutons à un résultat. Enfin, nous retournons ce résultat divisé par la taille de la liste.
  • Troisième méthode :

Code : C#
 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
static int CalculSommeIntersection()
{
    List<int> multiplesDe3 = new List<int>();
    List<int> multiplesDe5 = new List<int>();

    for (int i = 3; i <= 100; i += 3)
    {
        multiplesDe3.Add(i);
    }
    for (int i = 5; i <= 100; i += 5)
    {
        multiplesDe5.Add(i);
    }

    int somme = 0;
    foreach (int m3 in multiplesDe3)
    {
        foreach (int m5 in multiplesDe5)
        {
            if (m3 == m5)
                somme += m3;
        }
    }
    return somme;
}


Peut-être la plus compliquée... :)

On commence par créer nos deux listes de multiples. Comme je vous avais conseillé, j’utilise une boucle for qui commence à trois avec un incrément de 3. Comme ça, je suis sûr d’avoir tous les multiples de 3 dans ma liste. C’est le même principe pour les multiples de 5, sachant que dans les deux cas, la condition de sortie est quand l’indice est supérieur à 100.

Ensuite, j’ai mes deux boucles imbriquées où je compare les deux valeurs et si elles sont égales, je rajoute la valeur à la somme globale que je renvoie en fin de méthode.
Pour bien comprendre ce qu’il se passe dans les boucles imbriquées, il faut comprendre que nous allons parcourir une unique fois la liste multiplesDe3 mais que nous allons parcourir autant de fois la liste multipleDe5 qu’il y a d’éléments dans la liste multipleDe3, c’est-à-dire 33 fois.
Ce n’est sans doute pas facile de le concevoir dès le début, mais pour vous aider, vous pouvez essayer de vous faire l’algorithme dans la tête :

  • On rentre dans la boucle qui parcoure la liste multiplesDe3
  • m3 vaut 3
  • On rentre dans la boucle qui parcoure la liste multiplesDe5
  • m5 vaut 5
  • On compare 3 à 5, ils sont différents
  • On passe à l’itération suivante de la liste multiplesDe5
  • m5 vaut 10
  • On compare 3 à 10, ils sont différents
  • Etc … jusqu’à ce qu’on ait fini de parcourir la liste des multiplesDe5
  • On passe à l’itération suivante de la liste multiplesDe3
  • m3 vaut 6
  • On rentre dans la boucle qui parcoure la liste multiplesDe5
  • m5 vaut 5
  • On compare 6 à 5, ils sont différents
  • On passe à l’itération suivante de la liste multiplesDe5
  • Etc …

Aller plus loin

Vous avez remarqué que dans la deuxième méthode, j’utilise une boucle foreach pour parcourir la liste :

Code : C#
1
2
3
4
5
6
7
8
9
static double CalculMoyenne(List<double> liste)
{
    double somme = 0;
    foreach (double valeur in liste)
    {
        somme += valeur;
    }
    return somme / liste.Count;
}

Il est aussi possible de parcourir les listes avec un for et d’accéder aux éléments de la liste avec un indice, comme on le fait pour un tableau.
Ce qui donnerait :

Code : C#
1
2
3
4
5
6
7
8
9
static double CalculMoyenne(List<double> liste)
{
    double somme = 0;
    for (int i = 0; i < liste.Count; i++)
    {
        somme += liste[i];
    }
    return somme / liste.Count;
}


Notez qu’on se sert de liste.Count pour obtenir la taille de la liste et qu’on accède à l’élément courant avec liste[i].
Je reconnais que la boucle foreach est plus explicite, mais cela peut être utile de connaitre le parcours d’une liste avec la boucle for. A vous de voir.

Vous aurez également noté ma technique pour avoir des multiples de 3 et de 5. Il y en a plein d’autres. Je pense à une en particulier et qui fait appel à des notions que nous avons vu auparavant : la division entière et la division de double.

Pour savoir si un nombre est un multiple d’un autre, il suffit de les diviser entre eux et de voir s’il y a un reste à la division. Pour ce faire, on peut le faire de deux façons. Soit en comparant la division entière avec la division « double » et en vérifiant que le résultat est le même. Si le résultat est le même, c’est qu’il n’y a pas de chiffres après la virgule :

Code : C#
1
2
3
4
5
6
7
for (int i = 1; i <= 100; i++)
{
    if (i / 3 == i / 3.0)
        multiplesDe3.Add(i);
    if (i / 5 == i / 5.0)
        multiplesDe5.Add(i);
}


Cette technique fait appel à des notions que nous n’avons pas encore vues : comment cela se fait-il qu’on puisse comparer un entier à un double ? Nous le verrons un peu plus tard.


L'autre solution est d'utiliser l’opérateur modulo que nous avons vu précédemment qui fait justement ça :

Code : C#
1
2
3
4
5
6
7
for (int i = 1; i <= 100; i++)
{
    if (i % 3 == 0)
        multiplesDe3.Add(i);
    if (i % 5 == 0)
        multiplesDe5.Add(i);
}


L’avantage de ces deux techniques est qu’on peut construire les deux listes de multiples en une seule boucle.

Voilà, c’est fini pour ce TP. N’hésitez pas à vous faire la main sur ces boucles car il est fondamental que vous les maitrisiez. Faites-vous plaisir, tentez de les repousser dans leurs limites, essayez de résoudre d’autres problèmes, ... L’important, c’est de pratiquer.
Chapitre précédent Sommaire Chapitre suivant

Partager

23 commentaires pour "TP : Calculs en boucle"
Note moyenne : 3.05 / 4 (230 votes)
Pseudo Commentaire
Hors ligne Helgrind444 # Posté le 14/04/2012 à 23:41:04
Avatar

Avis : Très bon

Pareil, je dois avouer que j'ai peiné pour ce TP.
Faut dire que le C# est mon premier langage de prog', donc je suppose que c'est normal.
J'ai donc relu toute la partie très rapidement et je me suis arrêté sur ce qui me gênait le plus (les méthodes et les boucles).
J'ai réessayé... bon, j'irais pas dire que j'ai réussi à faire le bon code, mais je voyais à peu près comment faire pour chaque même si je peinais, donc bon ça allait.

Bref, Nihak... faut persévérer :p

Je sais mapper sur Source :D
HTML/CSS, c'est fait !
En route pour le C# !

Merci le SDZ ! :p
 
Hors ligne gfox78 # Posté le 15/04/2012 à 11:08:08
Avatar

Avis : Très bon

Ville : Marcq
Pays : France métropolitaine

Pour la troisième partie du TP, la solution proposée par l'auteur permet de limiter le nombre de boucles pour la constitution des deux premières listes, en jouant sur l'incrément des boucles "for" (55 boucles au total pour 'borne = 100'). Cependant tout l'avantage est perdu dans la recherche des intersections (34*21 = 714) boucles "foreach" pour 'borne = 100', mais déjà aussi 67134 boucles (334*201) pour 'borne = 1000'.

Pour éviter cette fastidieuse comparaison entre les deux listes, je propose d'introduire la variable "verif" au sein d'une seule boucle dans la solution suivante:
Secret (cliquez pour afficher)

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void ManipList()
        { 
            int verif = 0;
            int somme = 0;
            int borne = 100;
            List<int> ent3 = new List<int>();
            List<int> ent5 = new List<int>();
            List<int> intersection = new List<int>();
            for (int n = 0; n <= borne; n++)
            {
                if(n % 3 == 0) { ent3.Add(n); verif++; }
                if(n % 5 == 0) { ent5.Add(n); verif++; }
                if (verif == 2) { intersection.Add(n); somme += n; }
                verif = 0;
            }
            Console.WriteLine("Il y a " + intersection.Count + " intersections" + " et leur somme est : " + somme);
            foreach (int elem in intersection) Console.WriteLine(elem);
        }       
    }
}


Cela dit, en terme de performance, les 2 solutions sont sans doute presque équivalentes pour une faible valeur de "borne" mais doivent rapidement se différencier si "borne" a une grande valeur.

A ce propos, Virtual C# offre-t-il un système pour évaluer les performances des programmes en temps d'exécution et en utilisation mémoire ?
Hors ligne nico.pyright # Posté le 15/04/2012 à 16:35:16
Groupe : Auteurs

Visual Studio dispose d'un profiler mais uniquement dans la version Team System (payante), il s'agit de Visual Studio Team System Profiler.
D'autres profiler existent comme Aqtime qui dispose d'une version gratuite limitée, ou encore dottrace de Jetbrains
 
Hors ligne gfox78 # Posté le 17/04/2012 à 12:51:09
Avatar

Avis : Très bon

Ville : Marcq
Pays : France métropolitaine

Merci de ces renseignements sur les profilers.
Je ne perds cependant pas de vue que les TPs de ce tutoriel ont pour but de nous faire examiner le maximum de possibilités du C# (ici les boucles) et non d'optimiser les programmes.
Hors ligne jpnapo # Posté le 10/05/2012 à 03:46:12

Bonjour,

Respectant l'esprit de la troisième partie du TP, j'ai travaillé sur les listes sans utiliser des méthodes que nous n'avons pas encore abordées pour l'instant.
Merci au passage à geek1983 pour la méthode "Intersect" et à Darioo2 pour la méthode "Contains".

Pour ma part, j'ai créé une première méthode permettant de générer des multiples de nombres (ici 3 et 5) pour une plage donnée (ici 1 à 100).
Mes listes de multiples de 3 et 5 ont donc eté générées à partir de cette méthode, appelée à 2 reprises de la façon suivante :

Code : C#
1
2
3
4
5
// Création de la liste des multiples de 3 de 1 à 100
List<int> listMultiple3 = GetMultiple(3, 1, 100);

// Création de la liste des multiples de 5 de 1 à 100
List<int> listMultiple5 = GetMultiple(5, 1, 100);


La méthode créée peut paraître un peu compliquée, mais en fait, il n'en est rien.
Comme Nico, j'ai choisi d'utiliser le pas (3, 5) de la boucle "for" pour générer mes listes de multiples.
Ceci dans le but d'optimiser la rapidité du code et éviter ainsi d'intégrer systématiquement un test "if" et le calcul d'un modulo dans la boucle.

Par contre, ayant choisi de passer en paramètres une plage d'entiers quelconque, il m'a fallu calculer le premier multiple à partir de la borne inférieure.

Pour cela, il suffit de calculer le modulo de la borne inférieure divisée par le multiple choisi.
Si le reste est égal à 0, la borne inférieure est déjà un multiple.
Dans le cas contraire, on rajoute le multiple auquel on retranche le reste de la division.

Exemple : Multiple de 3 / Plage de 1 à 100 :
1 modulo 3 --> reste 1, donc on ajoute 3 à 1 auquel on retranche 1 --> 3

Exemple : Multiple de 5 / Plage de 4 à 100 :
4 modulo 5 --> reste 4, donc on rajoute 5 à 4 auquel on retranche 4 --> 5

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
static List<int> GetMultiple(int parMultiple, int parLowerBound, int parUpperBound)
{
    // Recherche du premier multiple
   int divisionRemainder= parLowerBound % parMultiple;
   if (divisionRemainder != 0)
       parLowerBound = parLowerBound + parMultiple - divisionRemainder;

   // Création de la liste
   List<int> myList = new List<int>();
   for(int i=parLowerBound; i<=parUpperBound; i+=parMultiple)
   {
        myList.Add(i);
   }

   // La liste est renvoyée par la méthode
   return myList;
}


Les 2 listes ainsi obtenues sont passées en paramètres de la méthode calculant la somme des multiples communs à celles-ci :

Code : C#
1
2
// Somme des multiples communs aux 2 listes
Console.WriteLine("Somme : " +ListIntersectionSum(listMultiple3, listMultiple5));


Pour cette méthode, j'ai fait en sorte d'éviter la pénalisante imbrication de boucles proposée en correction, comme l'a justement fait remarquer gfox78.
Les 2 listes ayant été générées dans l'ordre croissant, j'ai opté pour un parcours séquentiel de celles-ci en incrémentant de façon appropriée les indices de chaque liste en fonction du résultat des comparaisons.

Code : C#
 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
static int ListIntersectionSum(List<int> parListOne, List<int> parListTwo)
{
    // Initialisations
    int i = 0;                     // Indice de la liste 1
    int j = 0;                     // Indice de la liste 2
    int sum =0;                    // Sommes des multiples communs

    // Boucle tant que l'on a pas atteint les limites des tableaux
    while (i < parListOne.Count  && j < parListTwo.Count)
    {
        // Si les multiples sont communs aux 2 listes
        if (parListOne[i] == parListTwo[j]) 
        {
            sum += parListOne[i];  // Addition des multiples communs
            i++;                   // Incrémentation de l'indice de la liste 1
            j++;                   // Incrémentation de l'indice de la liste 2
        }
        else
        {
            // Si l'élément de la liste 1 est strictement inférieur à celui de la liste 2
            if ((parListOne[i] < parListTwo[j]))    
                i++;               // Incrémentation de l'indice de la liste 1
            else
                j++;               // Incrémentation de l'indice de la liste 2
        }
    }

    // La somme est renvoyée par la méthode
    return sum;
}


Pour conclure, je dirais que la performance d'un algorithme n'est pas forcément liée à sa longueur : en effet, pour reprendre la comparaison de gfox78, 46 passages dans la boucle sont nécessaires pour des multiples de 1 à 100 et seulement 466 pour des multiples de 1 à 1000...


Bravo pour le zCode qui est vraiment génial ;) et merci encore pour le tutoriel. Bonne continuation. Amicalement.

Voir tous les commentaires