--- 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é