diff --git a/slides/cours_5.md b/slides/cours_5.md new file mode 100644 index 0000000000000000000000000000000000000000..bd398a7e5002e877804cda75181221342e3c0063 --- /dev/null +++ b/slides/cours_5.md @@ -0,0 +1,499 @@ +--- +title: "Introduction aux algorithmes" +date: "2021-10-13" +patat: + eval: + tai: + command: fish + fragment: false + replace: true + ccc: + command: fish + fragment: false + replace: true + images: + backend: auto +... + + +# Représentation des nombres (1/2) + +* Le nombre `247`. + +## Nombres décimaux: Les nombres en base 10 + ++--------+--------+--------+ +| $10^2$ | $10^1$ | $10^0$ | ++--------+--------+--------+ +| `2` | `4` | `7` | ++--------+--------+--------+ + +$$ +247 = 2\cdot 10^2 + 4\cdot 10^1 + 7\cdot 10^0. +$$ + +# Représentation des nombres (2/2) + +* Le nombre `11110111`. + +## Nombres binaires: Les nombres en base 2 + ++-------+-------+-------+-------+-------+-------+-------+-------+ +| $2^7$ | $2^6$ | $2^5$ | $2^4$ | $2^3$ | $2^2$ | $2^1$ | $2^0$ | ++-------+-------+-------+-------+-------+-------+-------+-------+ +| `1` | `1` | `1` | `1` | `0` | `1` | `1` | `1` | ++-------+-------+-------+-------+-------+-------+-------+-------+ + +$$ +1\cdot 2^7 + 1\cdot 2^6 +1\cdot 2^5 +1\cdot 2^4 +0\cdot 2^3 +1\cdot 2^2 ++1\cdot 2^1 +1\cdot 2^0 +$$ + +. . . + +$$ += 247. +$$ + +# Conversion de décimal à binaire (1/2) + +## Convertir 11 en binaire? + +. . . + +* On décompose en puissances de 2 en partant de la plus grande possible + + ``` + 11 / 8 = 1, 11 % 8 = 3 + 3 / 4 = 0, 3 % 4 = 3 + 3 / 2 = 1, 3 % 2 = 1 + 1 / 1 = 1, 1 % 1 = 0 + ``` +* On a donc + + $$ + 1011 \Rightarrow 1\cdot 2^3 + 0\cdot 2^2 + 1\cdot 2^1 + 1\cdot + 2^0=11. + $$ + +# Conversion de décimal à binaire (2/2) + +## Convertir un nombre arbitraire en binaire: 247? + +* Par groupe établir un algorithme. + +. . . + +## Algorithme + +1. Initialisation + + ```C + num = 247 + while (2^N < num) { + N += 1 + } + ``` + +. . . + +2. Boucle + + ```C + while (N >= 0) { + bit = num / 2^N + num = num % 2^N + N += 1 + } + ``` + +# Les additions en binaire + +Que donne l'addition `1101` avec `0110`? + +* L'addition est la même que dans le système décimal + + ``` + 1101 8+4+0+1 = 13 + + 0110 + 0+4+2+0 = 6 + ------- ----------------- + 10011 16+0+0+2+1 = 19 + ``` +* Les entiers sur un ordinateur ont une précision **fixée** (ici 4 bits). +* Que se passe-t-il donc ici? + +. . . + +## Dépassement de capacité: le nombre est "tronqué" + +* `10011 (19) -> 0011 (3)`. +* On fait "le tour"." + +# Entier non-signés minimal/maximal + +* Quel est l'entier non-signé maximal représentable avec 4 bit? + +. . . + +$$ +(1111)_2 = 8+4+2+1 = 15 +$$ + +* Quel est l'entier non-signé minimal représentable avec 4 bit? + +. . . + +$$ +(0000)_2 = 0+0+0+0 = 0 +$$ + +* Quel est l'entier non-signé min/max représentable avec N bit? + +. . . + +$$ +0\mbox{ et }2^N-1. +$$ + +* Donc `uint32_t?` maximal est? + +. . . + +$$ +4294967295 +$$ + + +# Les multiplications en binaire (1/2) + +Que donne la multiplication de `1101` avec `0110`? + +* L'addition est la même que dans le système décimal + + ``` + 1101 13 + * 0110 * 6 + --------- -------------- + 0000 78 + 11010 + 110100 + + 0000000 + --------- -------------- + 1001110 64+0+0+8+4+2+0 + ``` + +# Les multiplications en binaire (2/2) + +## Que fait la multiplication par 2? + +. . . + +* Décalage de un bit vers la gauche! + + ``` + 0110 + * 0010 + --------- + 0000 + + 01100 + --------- + 01100 + ``` + +. . . + +## Que fait la multiplication par $2^N$? + +. . . + +* Décalade de $N$ bits vers la gauche! + +# Entiers signés (1/2) + +Pas de nombres négatifs encore... + +## Comment faire? + +. . . + +## Solution naïve: + +* On ajoute un bit de signe (le bit de poids fort): + + ``` + 00000010: +2 + 10000010: -2 + ``` + +## Problèmes? + +. . . + +* Il y a deux zéros (pas trop grave): `10000000` et `00000000` +* Les additions différentes que pour les non-signés (très grave) + + ``` + 00000010 2 + + 10000100 + -4 + ---------- ---- + 10000110 = -6 != -2 + ``` + +# Entiers signés (2/2) + +## Beaucoup mieux + +* Complément à un: + * on inverse tous les bits: `1001 => 0110`. + +## Encore un peu mieux + +* Complément à deux: + * on inverse tous les bits, + * on ajoute 1 (on ignore les dépassements). + +. . . + +* Comment écrit-on `-4` en 8 bits? + +. . . + +``` + 4 = 00000100 + ________ + -4 => 00000100 + + 11111011 + + 00000001 + ---------- + 11111100 +``` + +# Le complément à 2 (1/2) + +## Questions: + +* Comment on écrit `+0` et `-0`? +* Comment calcule-t-on `2 + (-4)`? +* Quel est le complément à 2 de `1000 0000`? + +. . . + +## Réponses + +* Comment on écrit `+0` et `-0`? + + ``` + +0 = 00000000 + -0 = 11111111 + 00000001 = 100000000 => 00000000 + ``` +* Comment calcule-t-on `2 + (-4)`? + + ``` + 00000010 2 + + 11111100 + -4 + ---------- ----- + 11111110 -2 + ``` +* En effet + + ``` + 11111110 => 00000001 + 00000001 = 00000010 = 2. + ``` + +# Le complément à 2 (1/2) + +## Quels sont les entiers représentables en 8 bits? + +. . . + +``` +01111111 => 127 +10000000 => -128 // par définition +``` + +## Quels sont les entiers représentables sur $N$ bits? + +. . . + +$$ +-2^{N-1} ... 2^{N-1}-1. +$$ + +## Remarque: dépassement de capacité en `C` + +* Comportement indéfini! + +# Nombres à virgule (1/N) + +## Comment manipuler des nombres à virgule? + +$$ +0.1 + 0.2 = 0.3. +$$ + +Facile non? + +. . . + +## Et ça? + +```C +#include <stdio.h> +#include <stdlib.h> +int main(int argc, char *argv[]) { + float a = atof(argv[1]); + float b = atof(argv[2]); + printf("%.10f\n", (double)(a + b)); +} +``` + +. . . + +## Que se passe-t-il donc? + +# Nombres à virgule (2/N) + +## Nombres à virgule fixe + ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ +| $2^3$ | $2^2$ | $2^1$ | $2^0$ | `.` | $2^{-1}$ | $2^{-2}$ | $2^{-3}$ | $2^{-4}$ | ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ +| `1` | `0` | `1` | `0` | `.` | `0` | `1` | `0` | `1` | ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ + +$$ +2^3+2^1+\frac{1}{2^2}+\frac{1}{2^4} = 8+2+0.5+0.0625=10.5625. +$$ + +## Limites + +* Nombres de $0=0000.0000$ à $15.9375=1111.1111$. +* Beaucoup de "trous" (au moins $0.0625$) entre deux nombres. + +<!-- # TODO -- + +<!-- ## Entiers, entiers non-signés --> + +<!-- ## Complément à 1, 2 --> + +<!-- ## Nombres à virgule flottante, simple/double précision --> + +# Types composés: `struct`{.C} (1/6) + +## Fractions + +* Numérateur: `int num`; +* Dénominateur: `int denom`. + +## Addition + +```C +int num1 = 1, denom1 = 2; +int num2 = 1, denom2 = 3; +int num3 = num1 * denom2 + num2 * denom1; +int denom3 = denom1 * denom2; +``` + +## Pas super pratique.... + +# Types composés: `struct`{.C} (2/6) + +## On peut faire mieux + +* Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}. + +```C +struct fraction { // déclaration du type + int32_t num, denom; +} + +struct fraction frac; // déclaration de frac +``` + +# Types composés: `struct`{.C} (3/6) + +## Simplifications + +- `typedef`{.C} permet de définir un nouveau type. + + ```C + typedef unsinged int uint; + typedef struct fraction fraction_t; + typedef struct fraction { + int32_t num, denom; + } fraction_t; + ``` +- L'initialisation peut aussi se faire avec + + ```C + fraction_t frac = {1, -2}; // num = 1, denom = -2 + fraction_t frac = {.denom = 1, .num = -2}; + fraction_t frac = {.denom = 1}; // argl! .num non initialisé + fraction_t frac2 = frac; // copie + ``` + +# Types composés: `struct`{.C} (4/6) + +## Pointeurs + +- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}. +- Les champs sont accessible avec le sélecteur `->`{.C} + + ```C + fraction_t *frac; // on crée un pointeur + frac->num = 1; // seg fault... + frac->denom = -1; // mémoire pas allouée. + ``` + +{width=50%} + +# Types composés: `struct`{.C} (5/6) + +## Initialisation + +- Avec le passage par **référence** on peut modifier un struct *en place*. +- Les champs sont accessible avec le sélecteur `->`{.C} + + ```C + void fraction_init(fraction_t *frac, + int32_t re, int32_t im) + { + // frac a déjà été allouée + frac->num = frac; + frac->denom = denom; + } + int main() { + fraction_t frac; // on alloue une fraction + fraction_init(&frac, 2, -1); // on l'initialise + } + ``` + +# Types composés: `struct`{.C} (6/6) + +## Initialisation version copie + +* On peut allouer une fraction, l'initialiser et le retourner. +* La valeur retournée peut être copiée dans une nouvelle structure. + + ```C + fraction_t fraction_create(int32_t re, int32_t im) { + fraction_t frac; + frac.num = re; + frac.denom = im; + return frac; + } + int main() { + // on crée une fraction et on l'initialise + // en copiant la fraction créé par fraction_create + // deux allocation et une copie + fraction_t frac = fraction_create(2, -1); + } + ``` + +# TODO jusqu'aux vacances + +* Refactorisation +* Tris et complexité +* Récursivité