diff --git a/slides/cours_3.md b/slides/cours_3.md new file mode 100644 index 0000000000000000000000000000000000000000..51c998b6bc330a03bce5d6bd2f2b5a21e1440c5b --- /dev/null +++ b/slides/cours_3.md @@ -0,0 +1,520 @@ +--- +title: "Introduction aux algorithmes" +date: "2021-10-06" +patat: + eval: + tai: + command: fish + fragment: false + replace: true + ccc: + command: fish + fragment: false + replace: true + images: + backend: auto +--- + +# Rappel (1/2) + +## Quels algos avons-nous vu la semaine passée? + +. . . + +* L'algorithme de la factorielle. +* L'algorithme du PPCM. +* Le début de l'algorithme du PGCD. + +# Rappel (2/2) + +## Algorithme du PPCM? + +. . . + +```C +int main() { + int m = 15, n = 12; + int mult_m = m, mult_n = n; + while (mult_m != mult_n) { + if (mult_m > mult_n) { + mult_n += n; + } else { + mult_m += m; + } + } + printf("Le ppcm de %d et %d est %d\n", n, m, mult_m); +} +``` + +# Le calcul du PGCD (1/5) + +## Définition + +Le plus grand commun diviseur (PGCD) de deux nombres entiers non nuls est le +plus grand entier qui les divise en même temps. + +## Exemples: + +```C +PGCD(3, 4) = 1, +PGCD(4, 6) = 2, +PGCD(5, 15) = 5. +``` + +. . . + +## Mathématiquement + +Décomposition en nombres premiers: + +$$ +36 = 2^2\cdot 3^2,\quad 90=2\cdot 5\cdot 3^2, +$$ +On garde tous les premiers à la puissance la plus basse +$$ +PGCD(36, 90)=2^{\min{1,2}}\cdot 3^{\min{2,2}}\cdot 5^{\min{0,1}}=18. +$$ + +# Le calcul du PGCD (2/5) + +## Algorithme + +Par groupe de 3 (5-10min): + +* réfléchissez à un algorithme alternatif donnant le PGCD de deux nombres; +* écrivez l'algorithme en pseudo-code. + +. . . + +## Exemple d'algorithme + +```C +PGCD(36, 90): +90 % 36 != 0 // otherwise 36 would be PGCD +90 % 35 != 0 // otherwise 35 would be PGCD +90 % 34 != 0 // otherwise 34 would be PGCD +... +90 % 19 != 0 // otherwise 19 would be PGCD +90 % 18 == 0 // The end! +``` + +* 18 modulos, 18 assignations, et 18 comparaisons. + +# Le calcul du PGCD (3/5) + +## Transcrivez cet exemple en algorithme (groupe de 3) et codez-le (5-10min)! + +. . . + +## Optimisation + +* Combien d'additions / comparaisons au pire? +* Un moyen de le rendre plus efficace? + +. . . + +## Tentative de correction + +```C +void main() { + int n = 90, m = 78; + int gcd = 1; + for (int div = n; div >= 2; div--) { // div = m, sqrt(n) + if (n % div == 0 && m % div == 0) { + gcd = div; + break; + } + } + printf("Le pgcd de %d et %d est %d\n", n, m, gcd); +} +``` + +# Le calcul du PGCD (4/5) + +## Réusinage: l'algorithme d'Euclide + +`Dividende = Diviseur * Quotient + Reste` + +```C +PGCD(35, 60): +35 = 60 * 0 + 35 // 60 -> 35, 35 -> 60 +60 = 35 * 1 + 25 // 35 -> 60, 25 -> 35 +35 = 25 * 1 + 10 // 25 -> 35, 20 -> 25 +25 = 10 * 2 + 5 // 10 -> 25, 5 -> 10 +10 = 5 * 2 + 0 // PGCD = 5! +``` + +. . . + +## Algorithme + +Par groupe de 3 (5-10min): + +* analysez l'exemple ci-dessus; +* transcrivez le en pseudo-code. + +# Le calcul du PGCD (5/5) + +## Pseudo-code + +```C +entier pgcd(a, b) { + tmp_n = n + tmp_m = m + tant que (tmp_m ne divise pas tmp_n) { + tmp = tmp_n + tmp_n = tmp_m + tmp_m = tmp modulo tmp_m + } + retourne tmp_m +} +``` + +# Le code du PGCD de 2 nombres + +## Implémentez le pseudo-code et postez le code sur matrix (5min). + +. . . + +## Un corrigé possible + +```C +#include <stdio.h> +void main() { + int n = 90; + int m = 78; + printf("n = %d et m = %d\n", n, m); + int tmp_n = n; + int tmp_m = m; + while (tmp_n%tmp_m > 0) { + int tmp = tmp_n; + tmp_n = tmp_m; + tmp_m = tmp % tmp_m; + } + printf("Le pgcd de %d et %d est %d\n", n, m, tmp_m); +} +``` + +# Quelques algorithmes simples + +* Remplissage d'un tableau et recherche de la valeur minimal +* Anagrammes +* Palindromes +* Crible d'ératosthène + +. . . + +* Ces algorithme nécessitent d'utiliser des **tableaux**. + +# Collections: tableaux statiques + +* Objets de même type: leur nombre est **connu à la compilation**; +* Stockés contigüement en mémoire (très efficace); + + ```C + #define SIZE 10 + int entiers[] = {2, 1, 4, 5, 7}; // taille 5, initialisé + int tab[3]; // taille 3, non initialisé + float many_floats[SIZE]; // taille 10, non initialisé + ``` +* Les indices sont numérotés de `0` à `taille-1`; + + ```C + int premier = entier[0]; // premier = 2 + int dernier = entier[4]; // dernier = 7 + ``` +* Les tableaux sont **non-initialisés** par défaut; +* Les bornes ne sont **jamais** vérifiées. + + ```C + int indetermine_1 = tab[1]; // undefined behavior + int indetermine_2 = entiers[5]; // UB + ``` + +# Remarques + +* Depuis `C99` possibilité d'avoir des tableaux dont la taille est *inconnue à + la compilation*; + + ```C + int size; + scanf("%d", &size); + char string[size]; + ``` + + . . . + +* Considéré comme une mauvaise pratique: que se passe-t-il si `size == 1e9`? +* On préfère utiliser l'allocation **dynamique** de mémoire pour ce genre de + cas-là (spoiler du futur du cours). + +# Initialisation + +* Les variables ne sont **jamais** initialisées en `C` par défaut. +* Question: Que contient le tableau suivant? + + ```C + double tab[4]; + ``` + +. . . + +* Réponse: On en sait absolument rien! +* Comment initialiser un tableau? + +. . . + +```C +#define SIZE 10 +double tab[SIZE]; +for (int i = 0; i < SIZE; ++i) { + tab[i] = rand() / (double)RAND_MAX * 10.0 - 5.0; + // tab[i] contient un double dans [-5;5] +} +``` + +# Recherche du minimum dans un tableau (1/2) + +## Problématique + +Trouver la valeur minimale contenue dans un tableau et l'indice de l'élément le plus petit. + +## Écrire un pseudo-code résolvant ce problème (groupe de 3), 2min + +. . . + +```C +index = 0 +min = tab[0] +for i in [1; SIZE] { + if min > tab[i] { + min = tab[i] + index = i + } +} +``` + +# Recherche du minimum dans un tableau (2/2) + +## Implémenter ce bout de code en C (groupe de 3), 4min + +. . . + +```C +int index = 0; +float min = tab[0]; +for (int i = 1; i < SIZE; ++i) { + if min > tab[i] { + min = tab[i]; + index = i; + } +} +``` + +# Tri par sélection (1/2) + +## Problématique + +Trier un tableau par ordre croissant. + +## Idée d'algorithme + +```C +ind = 0 +tant que (ind < SIZE-1) + Trouver le minimum du tableau, tab_min[ind:SIZE]. + Échanger tab_min avec tab[ind] + ind += 1 +``` + +# Tri par sélection (2/2) + +## Implémentation par groupe de 3 + +* Initialiser aléatoirement un tableau de `double` de taille 10; +* Afficher le tableau; +* Trier par sélection le tableau; +* Afficher le résultat trié; +* Vérifier algorithmiquement que le résultat est bien trié. + +# Un type de tableau particulier + +## Les chaînes de caractères + +```C +string = tableau + char + magie noire +``` + +# Le type `char`{.C} + +- Le type `char`{.C} est utilisé pour représenter un caractère. +- C'est un entier 8 bits signé. +- En particulier: + - Écrire + + ```C + char c = 'A'; + ``` + - Est équivalent à: + + ```C + char c = 65; + ``` +- Les fonctions d'affichage interprètent le nombre comme sa valeur ASCII. + +# Chaînes de caractères (strings) + +- Chaîne de caractère `==` tableau de caractères **terminé par la valeur** `'\0'`{.C} ou `0`{.C}. + +## Exemple + +```C +char *str = "HELLO !"; +char str[] = "HELLO !"; +``` + +Est représenté par + +| `char` | `H` | `E` | `L` | `L` | `O` | | `!` | `\0`| +|---------|------|------|------|------|------|------|------|-----| +| `ASCII` | `72` | `69` | `76` | `76` | `79` | `32` | `33` | `0` | + +. . . + +## A quoi sert le `\0`? + +. . . + +Permet de connaître la fin de la chaîne de caractères (pas le cas des autres +sortes de tableaux). + +# Syntaxe + +```C +char name[5]; +name[0] = 'P'; // = 70; +name[1] = 'a'; // = 97; +name[2] = 'u'; // = 117; +name[3] = 'l'; // = 108; +name[4] = '\0'; // = 0; +char name[] = {'P', 'a', 'u', 'l', '\0'}; +char name[5] = "Paul"; +char name[] = "Paul"; +char name[100] = "Paul is not 100 characters long."; +``` + +# Fonctions + +- Il existe une grande quantités de fonction pour la manipulation de chaînes de caractères dans `string.h`. +- Fonctions principales: + + ```C + // longueur de la chaîne (sans le \0) + size_t strlen(char *str); + // copie jusqu'à un \0 + char *strcpy(char *dest, const char *src); + // copie len char + char *strncpy(char *dest, const char *src, size_t len); + // compare len chars + int strncmp(char *str1, char *str2, size_t len); + // compare jusqu'à un \0 + int strcmp(char *str1, char *str2); + ``` + +- Pour avoir la liste complète: `man string`. + +. . . + +## Quels problèmes peuvent se produire avec `strlen`, `strcpy`, `strcmp`? + +# Les anagrammes + +## Définition + +Deux mots sont des anagrammes l'un de l'autre quand ils contiennent les mêmes +lettres mais dans un ordre différent. + +## Exemple + +| `t` | `u` | `t` | `u` | `t` | `\0` | ` ` | ` ` | +|------|------|------|------|------|------|------|-----| +| `t` | `u` | `t` | `t` | `u` | `\0` | ` ` | ` ` | + + +## Problème: Trouvez un algorithme pour déterminer si deux mots sont des anagrammes. + +# Les anagrammes + +## Il suffit de: + +1. Trier les deux mots. +2. Vérifier s'ils contiennent les mêmes lettres. + +## Implémentation en live (offerte par HepiaVPN) + +```C +int main() { // pseudo C + tri(mot1); + tri(mot2); + if egalite(mot1, mot2) { + // anagrammes + } else { + // pas anagrammes + } +} +``` + +<!-- TODO: Live implémentation hors des cours? --> + +# Les palindromes + +Mot qui se lit pareil de droite à gauche que de gauche à droite: + +. . . + +* rotor, kayak, ressasser, ... + +## Problème: proposer un algorithme pour détecter un palindrome + +. . . + +## Solution 1 + +```C +while (first_index < last_index { + if (mot[first_index] != mot [last_index]) { + return false; + } + first_index += 1; + last_index -= 1; +} +return true; +``` + +. . . + +## Solution 2 + +```C +mot_tmp = revert(mot); +return mot == mot_tmp; +``` + +# Crible d'Ératosthène + +Algorithme de génération de nombres premiers. + +## Exercice + +* À l'aide d'un tableau de booléens, +* Générer les nombres premiers plus petits qu'un nombre $N$ + +## Pseudo-code + +* Par groupe de trois, réfléchir à un algorithme. + +## Programme en C + +* Implémenter l'algorithme et le poster sur le salon `Element`. + +