Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cours
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
radhwan.hassine
cours
Commits
0ef352e3
Verified
Commit
0ef352e3
authored
1 year ago
by
orestis.malaspin
Browse files
Options
Downloads
Patches
Plain Diff
maj 2023 started
parent
6a00e745
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
slides/cours_6.md
+0
-347
0 additions, 347 deletions
slides/cours_6.md
slides/cours_7.md
+874
-0
874 additions, 0 deletions
slides/cours_7.md
with
874 additions
and
347 deletions
slides/cours_6.md
+
0
−
347
View file @
0ef352e3
...
@@ -597,350 +597,3 @@ int fib_imp(int n) {
...
@@ -597,350 +597,3 @@ int fib_imp(int n) {
}
}
```
```
# Exponentiation rapide ou indienne (1/4)
## But: Calculer $x^n$
*
Quel est l'algorithmie le plus simple que vous pouvez imaginer?
. . .
```
C
int pow(int x, int n) {
if (0 == n) {
return 1;
}
for (int i = 1; i < n; ++i) {
x = x * x; // x *= x
}
return x;
}
```
*
Combien de multiplication et d'assignations en fonction de
`n`
?
. . .
*
`n`
assignations et
`n`
multiplications.
# Exponentiation rapide ou indienne (2/4)
*
Proposez un algorithme naïf et récursif
. . .
```
C
int pow(x, n) {
if (n != 0) {
return x * pow(x, n-1);
} else {
return 1;
}
}
```
# Exponentiation rapide ou indienne (3/4)
## Exponentiation rapide ou indienne de $x^n$
*
Écrivons $n=
\s
um_{i=0}^{d-1}b_i 2^i,
\
b_i=
\{
0,1
\}
$ (écriture binaire sur $d$ bits, avec
$d
\s
im
\l
og_2(n)$).
*
$$
x^n={x^{2^0}}^{b_0}
\c
dot {x^{2^1}}^{b_1}
\c
dots {x^{2^{d-1}}}^{b_{d-1}}.
$$
*
On a besoin de $d$ calculs pour les $x^{2^i}$.
*
On a besoin de $d$ calculs pour évaluer les produits de tous les termes.
## Combien de calculs en terme de $n$?
. . .
*
$n$ est représenté en binaire avec $d$ bits $
\R
ightarrow d
\s
im
\l
og_2(n)$.
*
il y a $2
\l
og_2(n)
\s
im
\l
og_2(n)$ calculs.
# Exponentiation rapide ou indienne (4/4)
## Le vrai algorithme
*
Si n est pair: calculer $
\l
eft(x^{n/2}
\r
ight)^2$,
*
Si n est impair: calculer $x
\c
dot
\l
eft(x^{(n-1)/2}
\r
ight)^2$.
## Exercice: écrire l'algorithme récursif correspondant
. . .
```
C
double pow(double x, int n) {
if (1 == n) {
return x;
} else if (n % 2 == 0) {
return pow(x, n / 2) * pow(x, n/2);
} else {
return x * pow(x, (n-1));
}
}
```
# Efficacité d'un algorithmique
Comment mesurer l'efficacité d'un algorithme?
. . .
*
Mesurer le temps CPU,
*
Mesurer le temps d'accès à la mémoire,
*
Mesurer la place prise mémoire,
. . .
Dépendant du
**matériel**
, du
**compilateur**
, des
**options de compilation**
, etc!
## Mesure du temps CPU
```
C
#include <time.h>
struct timespec tstart={0,0}, tend={0,0};
clock_gettime(CLOCK_MONOTONIC, &tstart);
// some computation
clock_gettime(CLOCK_MONOTONIC, &tend);
printf("computation about %.5f seconds\n",
((double)tend.tv_sec + 1e-9*tend.tv_nsec) -
((double)tstart.tv_sec + 1e-9*tstart.tv_nsec));
```
# Programme simple: mesure du temps CPU
## Preuve sur un [petit exemple](../source_codes/complexity/sum.c)
```
bash
source_codes/complexity
$
make bench
RUN ONCE
-O0
the computation took about 0.00836 seconds
RUN ONCE
-O3
the computation took about 0.00203 seconds
RUN THOUSAND TIMES
-O0
the computation took about 0.00363 seconds
RUN THOUSAND TIMES
-O3
the computation took about 0.00046 seconds
```
Et sur votre machine les résultats seront
**différents**
.
. . .
## Conclusion
*
Nécessité d'avoir une mesure indépendante du/de la
matériel/compilateur/façon de mesurer/météo.
# Analyse de complexité algorithmique (1/4)
*
On analyse le
**temps**
pris par un algorithme en fonction de la
**
taille de
l'entrée
**
.
## Exemple: recherche d'un élément dans une liste triée de taille N
```
C
int sorted_list[N];
bool in_list = is_present(N, sorted_list, elem);
```
*
Plus
`N`
est grand, plus l'algorithme prend de temps sauf si...
. . .
*
l'élément est le premier de la liste (ou à une position toujours la même).
*
ce genre de cas pathologique ne rentre pas en ligne de compte.
# Analyse de complexité algorithmique (2/4)
## Recherche linéaire
```
C
bool is_present(int n, int tab[], int elem) {
for (int i = 0; i < n; ++i) {
if (tab[i] == elem) {
return true;
} else if (elem < tab[i]) {
return false;
}
}
return false;
}
```
*
Dans le
**meilleurs des cas**
il faut
`1`
comparaison.
*
Dans le
**pire des cas**
(élément absent p.ex.) il faut
`n`
comparaisons.
. . .
La
**complexité algorithmique**
est proportionnelle à
`N`
: on double la taille
du tableau $
\R
ightarrow$ on double le temps pris par l'algorithme.
# Analyse de complexité algorithmique (3/4)
## Recherche dichotomique
```
C
bool is_present_binary_search(int n, int tab[], int elem) {
int left = 0;
int right = n - 1;
while (left <= right) {
int mid = (right + left) / 2;
if (tab[mid] < elem) {
left = mid + 1;
} else if (tab[mid] > elem) {
right = mid - 1;
} else {
return true;
}
}
return false;
}
```
# Analyse de complexité algorithmique (4/4)
## Recherche dichotomique

](figs/Binary_search_complexity.svg){width=80%}
. . .
*
Dans le
**meilleurs de cas**
il faut
`1`
comparaison.
*
Dans le
**pire des cas**
il faut $
\l
og_2(N)+1$ comparaisons
. . .
## Linéaire vs dichotomique
*
$N$ vs $
\l
og_2(N)$ comparaisons logiques.
*
Pour $N=1000000$:
`1000000`
vs
`21`
comparaisons.
# Notation pour la complexité
## Constante de proportionnalité
*
Pour la recherche linéaire ou dichotomique, on a des algorithmes qui sont $
\s
im N$ ou $
\s
im
\l
og_2(N)$
*
Qu'est-ce que cela veut dire?
. . .
*
Temps de calcul est $t=C
\c
dot N$ (où $C$ est le temps pris pour une comparaisons sur une machine/compilateur donné)
*
La complexité ne dépend pas de $C$.
## Le $\mathcal{O}$ de Leibnitz
*
Pour noter la complexité d'un algorithme on utilise le symbole $
\m
athcal{O}$ (ou "grand Ô de").
*
Les complexités les plus couramment rencontrées sont
. . .
$$
\m
athcal{O}(1),
\q
uad
\m
athcal{O}(
\l
og(N)),
\q
uad
\m
athcal{O}(N),
\q
uad
\m
athcal{O}(
\l
og(N)
\c
dot N),
\q
uad
\m
athcal{O}(N^2),
\q
uad
\m
athcal{O}(N^3).
$$
# Ordres de grandeur
\b
egin{table}[!h]
\b
egin{center}
\c
aption{Valeurs approximatives de quelques fonctions usuelles de complexité.}
\m
edskip
\b
egin{tabular}{|c|c|c|c|c|}
\h
line
$
\l
og_2(N)$ & $
\s
qrt{N}$ & $N$ & $N
\l
og_2(N)$ & $N^2$
\\
\h
line
\h
line
$3$ & $3$ & $10$ & $30$ & $10^2$
\\
\h
line
$6$ & $10$ & $10^2$ & $6
\c
dot 10^2$ & $10^4$
\\
\h
line
$9$ & $31$ & $10^3$ & $9
\c
dot 10^3$ & $10^6$
\\
\h
line
$13$ & $10^2$ & $10^4$ & $1.3
\c
dot 10^5$ & $10^8$
\\
\h
line
$16$ & $3.1
\c
dot 10^2$ & $10^5$ & $1.6
\c
dot 10^6$ & $10^{10}$
\\
\h
line
$19$ & $10^3$ & $10^6$ & $1.9
\c
dot 10^7$ & $10^{12}$
\\
\h
line
\e
nd{tabular}
\e
nd{center}
\e
nd{table}
# Quelques exercices (1/3)
## Complexité de l'algorithme de test de primalité naïf?
```
C
for (i = 2; i < sqrt(N); ++i) {
if (N % i == 0) {
return false;
}
}
return true;
```
. . .
## Réponse
$$
\m
athcal{O}(
\s
qrt{N}).
$$
# Quelques exercices (2/3)
## Complexité de trouver le minimum d'un tableau?
```
C
int min = MAX;
for (i = 0; i < N; ++i) {
if (tab[i] < min) {
min = tab[i];
}
}
return min;
```
. . .
## Réponse
$$
\m
athcal{O}(N).
$$
# Quelques exercices (3/3)
## Complexité du tri par sélection?
```
C
int ind = 0
while (ind < SIZE-1) {
min = find_min(tab[ind:SIZE]);
swap(min, tab[ind]);
ind += 1
}
```
. . .
## Réponse
### `min = find_min`
$$
(N-1)+(N-2)+...+2+1=
\s
um_{i=1}^{N-1}i=N
\c
dot(N-1)/2=
\m
athcal{O}(N^2).
$$
## Finalement
$$
\m
athcal{O}(N^2
\m
box{ comparaisons}) +
\m
athcal{O}(N
\m
box{swaps})=
\m
athcal{O}(N^2).
$$
This diff is collapsed.
Click to expand it.
slides/cours_7.md
0 → 100644
+
874
−
0
View file @
0ef352e3
---
title
:
"
Récursion
et
tris"
date
:
"
2022-11-16"
---
# Exponentiation rapide ou indienne (1/4)
## But: Calculer $x^n$
*
Quel est l'algorithmie le plus simple que vous pouvez imaginer?
. . .
```
C
int pow(int x, int n) {
if (0 == n) {
return 1;
}
for (int i = 1; i < n; ++i) {
x = x * x; // x *= x
}
return x;
}
```
*
Combien de multiplication et d'assignations en fonction de
`n`
?
. . .
*
`n`
assignations et
`n`
multiplications.
# Exponentiation rapide ou indienne (2/4)
*
Proposez un algorithme naïf et récursif
. . .
```
C
int pow(x, n) {
if (n != 0) {
return x * pow(x, n-1);
} else {
return 1;
}
}
```
# Exponentiation rapide ou indienne (3/4)
## Exponentiation rapide ou indienne de $x^n$
*
Écrivons $n=
\s
um_{i=0}^{d-1}b_i 2^i,
\
b_i=
\{
0,1
\}
$ (écriture binaire sur $d$ bits, avec
$d
\s
im
\l
og_2(n)$).
*
$$
x^n={x^{2^0}}^{b_0}
\c
dot {x^{2^1}}^{b_1}
\c
dots {x^{2^{d-1}}}^{b_{d-1}}.
$$
*
On a besoin de $d$ calculs pour les $x^{2^i}$.
*
On a besoin de $d$ calculs pour évaluer les produits de tous les termes.
## Combien de calculs en terme de $n$?
. . .
*
$n$ est représenté en binaire avec $d$ bits $
\R
ightarrow d
\s
im
\l
og_2(n)$.
*
il y a $2
\l
og_2(n)
\s
im
\l
og_2(n)$ calculs.
# Exponentiation rapide ou indienne (4/4)
## Le vrai algorithme
*
Si n est pair: calculer $
\l
eft(x^{n/2}
\r
ight)^2$,
*
Si n est impair: calculer $x
\c
dot
\l
eft(x^{(n-1)/2}
\r
ight)^2$.
## Exercice: écrire l'algorithme récursif correspondant
. . .
```
C
double pow(double x, int n) {
if (1 == n) {
return x;
} else if (n % 2 == 0) {
return pow(x, n / 2) * pow(x, n/2);
} else {
return x * pow(x, (n-1));
}
}
```
# Tri rapide ou quicksort (1/8)
## Idée: algorithme `diviser pour régner` (`divide-and-conquer`)
*
Diviser: découper un problème en sous problèmes;
*
Régner: résoudre les sous-problèmes (souvent récursivement);
*
Combiner: à partir des sous problèmes résolu, calculer la solution.
## Le pivot
*
Trouver le
**pivot**
, un élément qui divise le tableau en 2, tels que:
1.
Éléments à gauche sont
**plus petits**
que le pivot.
2.
Élements à droite sont
**plus grands**
que le pivot.
# Tri rapide ou quicksort (2/8)
## Algorithme `quicksort(tableau)`
1.
Choisir le pivot et l'amener à sa place:
*
Les éléments à gauche sont plus petits que le pivot.
*
Les éléments à droite sont plus grand que le pivot.
2.
`quicksort(tableau_gauche)`
en omettant le pivot.
3.
`quicksort(tableau_droite)`
en omettant le pivot.
4.
S'il y a moins de deux éléments dans le tableau, le tableau est trié.
. . .
Compris?
. . .
Non c'est normal, faisons un exemple.
# Tri rapide ou quicksort (3/8)
\f
ootnotesize
Deux variables sont primordiales:
```
C
entier ind_min, ind_max; // les indices min/max des tableaux à trier
```

# Tri rapide ou quicksort (4/8)
\f
ootnotesize
Deux variables sont primordiales:
```
C
entier ind_min, ind_max; // les indices min/max des tableaux à trier
```
## Pseudocode: quicksort
```
python
rien
quicksort
(
entier
tableau
[],
entier
ind_min
,
entier
ind_max
)
si
(
longueur
(
tab
)
>
1
)
ind_pivot
=
partition
(
tableau
,
ind_min
,
ind_max
)
si
(
longueur
(
tableau
[
ind_min
:
ind_pivot
-
1
])
!=
0
)
quicksort
(
tableau
,
ind_min
,
pivot_ind
-
1
)
si
(
longueur
(
tableau
[
ind_pivot
+
1
:
ind_max
-
1
])
!=
0
)
quicksort
(
tableau
,
ind_pivot
+
1
,
ind_max
)
```
# Tri rapide ou quicksort (5/8)
\f
ootnotesize
## Pseudocode: partition
```
C
entier partition(entier tableau[], entier ind_min, entier ind_max)
pivot = tableau[ind_max] // choix arbitraire
i = ind_min
j = ind_max-1
tant que i < j:
en remontant i trouver le premier élément > pivot
en descendant j trouver le premier élément < pivot
échanger(tableau[i], tableau[j])
// les plus grands à droite
// mettre les plus petits à gauche
// on met le pivot "au milieu"
échanger(tableau[i], tableau[ind_max])
retourne i // on retourne l'indice pivot
```
# Tri rapide ou quicksort (6/8)
## Exercice: implémenter les fonctions `quicksort` et `partition`
. . .
```
C
void quicksort(int size, int array[size], int first,
int last)
{
if (first < last) {
int midpoint = partition(size, array, first, last);
if (first < midpoint - 1) {
quicksort(size, array, first, midpoint - 1);
}
if (midpoint + 1 < last) {
quicksort(size, array, midpoint + 1, last);
}
}
}
```
# Tri rapide ou quicksort (7/8)
\f
ootnotesize
## Exercice: implémenter les fonctions `quicksort` et `partition`
```
C
int partition(int size, int array[size], int first, int last) {
int pivot = array[last];
int i = first - 1, j = last;
do {
do {
i += 1;
} while (array[i] < pivot && i < j);
do {
j -= 1;
} while (array[j] > pivot && i < j);
if (j > i) {
swap(&array[i], &array[j]);
}
} while (j > i);
swap(&array[i], &array[last]);
return i;
}
```
# Tri rapide ou quicksort (8/8)
## Quelle est la complexité du tri rapide?
. . .
*
Pire des cas plus: $
\m
athcal{O}(N^2)$
*
Quand le pivot sépare toujours le tableau de façon déséquilibrée ($N-1$
éléments d'un côté $1$ de l'autre).
*
$N$ boucles et $N$ comparaisons $
\R
ightarrow N^2$.
*
Meilleur des cas (toujours le meilleur pivot): $
\m
athcal{O}(N
\c
dot
\l
og_2(N))$.
*
Chaque fois le tableau est séparé en $2$ parties égales.
*
On a $
\l
og_2(N)$ partitions, et $N$ boucles $
\R
ightarrow N
\c
dot
\l
og_2(N)$.
*
En moyenne: $
\m
athcal{O}(N
\c
dot
\l
og_2(N))$.
# L'algorithme à la main
## Exercice *sur papier*
*
Trier par tri rapide le tableau
`[5, -2, 1, 3, 10, 15, 7, 4]`
```
C
```
# Tri à bulle (1/4)
## Algorithme
*
Parcours du tableau et comparaison des éléments consécutifs:
-
Si deux éléments consécutifs ne sont pas dans l'ordre, ils sont échangés.
*
On recommence depuis le début du tableau jusqu'à avoir plus d'échanges à
faire.
## Que peut-on dire sur le dernier élément du tableau après un parcours?
. . .
*
Le plus grand élément est
**à la fin**
du tableau.
*
Plus besoin de le traiter.
*
A chaque parcours on s'arrête un élément plus tôt.
# Tri à bulle (2/4)
## Exemple

# Tri à bulle (3/4)
## Exercice: écrire l'algorithme (poster le résultat sur matrix)
. . .
```
C
rien tri_a_bulles(entier tableau[])
pour i de longueur(tableau)-1 à 1:
trié = vrai
pour j de 0 à i-1:
si (tableau[j] > tableau[j+1])
échanger(array[j], array[j+1])
trié = faux
si trié
retourner
```
# Tri à bulle (4/4)
## Quelle est la complexité du tri à bulles?
. . .
*
Dans le meilleurs des cas:
*
Le tableau est déjà trié: $
\m
athcal{O}(N)$ comparaisons.
*
Dans le pire des cas, $N
\c
dot (N-1)/2
\s
im
\m
athcal{O}(N^2)$:
$$
\s
um_{i=1}^{N-1}i
\m
box{ comparaison et }3
\s
um_{i=1}^{N-1}i
\m
box{ affectations
(swap)}
\R
ightarrow
\m
athcal{O}(N^2).
$$
*
En moyenne, $
\m
athcal{O}(N^2)$ ($N^2/2$ comparaisons).
# L'algorithme à la main
## Exercice *sur papier*
*
Trier par tri à bulles le tableau
`[5, -2, 1, 3, 10, 15, 7, 4]`
```
C
```
# Efficacité d'un algorithmique
Comment mesurer l'efficacité d'un algorithme?
. . .
*
Mesurer le temps CPU,
*
Mesurer le temps d'accès à la mémoire,
*
Mesurer la place prise mémoire,
. . .
Dépendant du
**matériel**
, du
**compilateur**
, des
**options de compilation**
, etc!
## Mesure du temps CPU
```
C
#include <time.h>
struct timespec tstart={0,0}, tend={0,0};
clock_gettime(CLOCK_MONOTONIC, &tstart);
// some computation
clock_gettime(CLOCK_MONOTONIC, &tend);
printf("computation about %.5f seconds\n",
((double)tend.tv_sec + 1e-9*tend.tv_nsec) -
((double)tstart.tv_sec + 1e-9*tstart.tv_nsec));
```
# Programme simple: mesure du temps CPU
## Preuve sur un [petit exemple](../source_codes/complexity/sum.c)
```
bash
source_codes/complexity
$
make bench
RUN ONCE
-O0
the computation took about 0.00836 seconds
RUN ONCE
-O3
the computation took about 0.00203 seconds
RUN THOUSAND TIMES
-O0
the computation took about 0.00363 seconds
RUN THOUSAND TIMES
-O3
the computation took about 0.00046 seconds
```
Et sur votre machine les résultats seront
**différents**
.
. . .
## Conclusion
*
Nécessité d'avoir une mesure indépendante du/de la
matériel/compilateur/façon de mesurer/météo.
# Analyse de complexité algorithmique (1/4)
*
On analyse le
**temps**
pris par un algorithme en fonction de la
**
taille de
l'entrée
**
.
## Exemple: recherche d'un élément dans une liste triée de taille N
```
C
int sorted_list[N];
bool in_list = is_present(N, sorted_list, elem);
```
*
Plus
`N`
est grand, plus l'algorithme prend de temps sauf si...
. . .
*
l'élément est le premier de la liste (ou à une position toujours la même).
*
ce genre de cas pathologique ne rentre pas en ligne de compte.
# Analyse de complexité algorithmique (2/4)
## Recherche linéaire
```
C
bool is_present(int n, int tab[], int elem) {
for (int i = 0; i < n; ++i) {
if (tab[i] == elem) {
return true;
} else if (elem < tab[i]) {
return false;
}
}
return false;
}
```
*
Dans le
**meilleurs des cas**
il faut
`1`
comparaison.
*
Dans le
**pire des cas**
(élément absent p.ex.) il faut
`n`
comparaisons.
. . .
La
**complexité algorithmique**
est proportionnelle à
`N`
: on double la taille
du tableau $
\R
ightarrow$ on double le temps pris par l'algorithme.
# Analyse de complexité algorithmique (3/4)
## Recherche dichotomique
```
C
bool is_present_binary_search(int n, int tab[], int elem) {
int left = 0;
int right = n - 1;
while (left <= right) {
int mid = (right + left) / 2;
if (tab[mid] < elem) {
left = mid + 1;
} else if (tab[mid] > elem) {
right = mid - 1;
} else {
return true;
}
}
return false;
}
```
# Analyse de complexité algorithmique (4/4)
## Recherche dichotomique

](figs/Binary_search_complexity.svg){width=80%}
. . .
*
Dans le
**meilleurs de cas**
il faut
`1`
comparaison.
*
Dans le
**pire des cas**
il faut $
\l
og_2(N)+1$ comparaisons
. . .
## Linéaire vs dichotomique
*
$N$ vs $
\l
og_2(N)$ comparaisons logiques.
*
Pour $N=1000000$:
`1000000`
vs
`21`
comparaisons.
# Notation pour la complexité
## Constante de proportionnalité
*
Pour la recherche linéaire ou dichotomique, on a des algorithmes qui sont $
\s
im N$ ou $
\s
im
\l
og_2(N)$
*
Qu'est-ce que cela veut dire?
. . .
*
Temps de calcul est $t=C
\c
dot N$ (où $C$ est le temps pris pour une comparaisons sur une machine/compilateur donné)
*
La complexité ne dépend pas de $C$.
## Le $\mathcal{O}$ de Leibnitz
*
Pour noter la complexité d'un algorithme on utilise le symbole $
\m
athcal{O}$ (ou "grand Ô de").
*
Les complexités les plus couramment rencontrées sont
. . .
$$
\m
athcal{O}(1),
\q
uad
\m
athcal{O}(
\l
og(N)),
\q
uad
\m
athcal{O}(N),
\q
uad
\m
athcal{O}(
\l
og(N)
\c
dot N),
\q
uad
\m
athcal{O}(N^2),
\q
uad
\m
athcal{O}(N^3).
$$
# Ordres de grandeur
\b
egin{table}[!h]
\b
egin{center}
\c
aption{Valeurs approximatives de quelques fonctions usuelles de complexité.}
\m
edskip
\b
egin{tabular}{|c|c|c|c|c|}
\h
line
$
\l
og_2(N)$ & $
\s
qrt{N}$ & $N$ & $N
\l
og_2(N)$ & $N^2$
\\
\h
line
\h
line
$3$ & $3$ & $10$ & $30$ & $10^2$
\\
\h
line
$6$ & $10$ & $10^2$ & $6
\c
dot 10^2$ & $10^4$
\\
\h
line
$9$ & $31$ & $10^3$ & $9
\c
dot 10^3$ & $10^6$
\\
\h
line
$13$ & $10^2$ & $10^4$ & $1.3
\c
dot 10^5$ & $10^8$
\\
\h
line
$16$ & $3.1
\c
dot 10^2$ & $10^5$ & $1.6
\c
dot 10^6$ & $10^{10}$
\\
\h
line
$19$ & $10^3$ & $10^6$ & $1.9
\c
dot 10^7$ & $10^{12}$
\\
\h
line
\e
nd{tabular}
\e
nd{center}
\e
nd{table}
# Quelques exercices (1/3)
## Complexité de l'algorithme de test de primalité naïf?
```
C
for (i = 2; i < sqrt(N); ++i) {
if (N % i == 0) {
return false;
}
}
return true;
```
. . .
## Réponse
$$
\m
athcal{O}(
\s
qrt{N}).
$$
# Quelques exercices (2/3)
## Complexité de trouver le minimum d'un tableau?
```
C
int min = MAX;
for (i = 0; i < N; ++i) {
if (tab[i] < min) {
min = tab[i];
}
}
return min;
```
. . .
## Réponse
$$
\m
athcal{O}(N).
$$
# Quelques exercices (3/3)
## Complexité du tri par sélection?
```
C
int ind = 0
while (ind < SIZE-1) {
min = find_min(tab[ind:SIZE]);
swap(min, tab[ind]);
ind += 1
}
```
. . .
## Réponse
### `min = find_min`
$$
(N-1)+(N-2)+...+2+1=
\s
um_{i=1}^{N-1}i=N
\c
dot(N-1)/2=
\m
athcal{O}(N^2).
$$
## Finalement
$$
\m
athcal{O}(N^2
\m
box{ comparaisons}) +
\m
athcal{O}(N
\m
box{swaps})=
\m
athcal{O}(N^2).
$$
# Tri par insertion (1/3)
## But
*
trier un tableau par ordre croissant
## Algorithme
Prendre un élément du tableau et le mettre à sa place parmis les éléments déjà
triés du tableau.

# Tri par insertion (2/3)
## Exercice: Proposer un algorithme (en C)
. . .
```
C
void tri_insertion(int N, int tab[N]) {
for (int i = 1; i < N; i++) {
int tmp = tab[i];
int pos = i;
while (pos > 0 && tab[pos - 1] > tmp) {
tab[pos] = tab[pos - 1];
pos = pos - 1;
}
tab[pos] = tmp;
}
}
```
# Tri par insertion (3/3)
## Question: Quelle est la complexité?
. . .
*
Parcours de tous les éléments ($N-1$ passages dans la boucle)
*
Placer: en moyenne $i$ comparaisons et affectations à l'étape $i$
*
Moyenne: $
\m
athcal{O}(N^2)$
. . .
*
Pire des cas, liste triée à l'envers: $
\m
athcal{O}(N^2)$
*
Meilleurs des cas, liste déjà triée: $
\m
athcal{O}(N)$
# L'algorithme à la main
## Exercice *sur papier*
*
Trier par insertion le tableau
`[5, -2, 1, 3, 10]`
```
C
```
# Problème des 8-reines
*
Placer 8 reines sur un échiquier de $8
\t
imes 8$.
*
Sans que les reines ne puissent se menacer mutuellement (92 solutions).
## Conséquence
*
Deux reines ne partagent pas la même rangée, colonne, ou diagonale.
*
Donc chaque solution a
**une**
reine
**par colonne**
ou
**ligne**
.
## Généralisation
*
Placer $N$ reines sur un échiquier de $N
\t
imes
N$.
-
Exemple de
**backtracking**
(retour en arrière) $
\R
ightarrow$ récursivité.

](./figs/fig_recursivite_8_reines.png){width=35%}
# Problème des 2-reines

{width=50%}
# Comment trouver les solutions?
*
On pose la première reine sur la première case disponible.
*
On rend inaccessibles toutes les cases menacées.
*
On pose la reine suivante sur la prochaine case non-menacée.
*
Jusqu'à ce qu'on puisse plus poser de reine.
*
On revient alors en arrière jusqu'au dernier coup où il y avait plus qu'une
possibilité de poser une reine.
*
On recommence depuis là.
. . .
*
Le jeu prend fin quand on a énuméré
*toutes*
les possibilités de poser les
reines.
# Problème des 3-reines

# Problème des 4-reines

# Problème des 4-reines, symétrie

# Problème des 5 reines
## Exercice: Trouver une solution au problème des 5 reines
*
Faire une capture d'écran / une photo de votre solution et la poster sur
matrix.
```
C
```
# Quelques observations sur le problème
*
Une reine par colonne au plus.
*
On place les reines sur des colonnes successives.
*
On a pas besoin de "regarder en arrière" (on place "devant" uniquement).
*
Trois étapes:
*
On place une reine dans une case libre.
*
On met à jour le tableau.
*
Quand on a plus de cases libres on "revient dans le temps" ou c'est qu'on
a réussi.
# Le code du problème des 8 reines (1/N)
## Quelle structure de données?
. . .
Une matrice de booléens fera l'affaire:
```
C
bool board[n][n];
```
## Quelles fonctionnalités?
. . .
```
C
// Pour chaque ligne placer la reine sur toutes les colonnes
// et compter les solutions
void nbr_solutions(board, column, counter);
// Copier un tableau dans un autre
void copy(board_in, board_out);
// Placer la reine à li, co et rendre inaccessible devant
void placer_devant(board, li, co);
```
# Le code du problème des 8 reines (2/N)
## Le calcul du nombre de solutions
```
C
// Calcule le nombre de solutions au problème des <n> reines
nbr_solutions(board, column, count)
// pour chaque ligne
// si la case libre
// si column < n - 1
// copier board dans un "new" board,
// y poser une reine
// et mettre à jour ce "new" board
// nbr_solutions(new_board, column+1, count)
// sinon
// on a posé la n-ème et on a gagné
// count += 1
```
# Le code du problème des 8 reines (3/N)
## Le calcul du nombre de solutions
```
C
// Placer une reine et mettre à jour
placer_devant(board, ligne, colonne)
// board est occupé à ligne/colonne
// toutes les cases des colonnes
// suivantes sont mises à jour
```
# Le code du problème des 8 reines (4/N)
## Compris? Alors écrivez le code et postez le!
. . .
## Le nombre de solutions
\f
ootnotesize
```
C
// Calcule le nombre de solutions au problème des <n> reines
void nb_sol(int n, bool board[n][n], int co, int *ptr_cpt) {
for (int li = 0; li < n; li++) {
if (board[li][co]) {
if (co < n-1) {
bool new_board[n][n]; // alloué à chaque nouvelle tentative
copy(n, board, new_board);
prises_devant(n, new_board, li, co);
nb_sol(n, new_board, co+1, ptr_cpt);
} else {
*ptr_cpt = (*ptr_cpt)+1;
}
}
}
}
```
# Le code du problème des 8 reines (5/N)
\f
ootnotesize
## Placer devant
```
C
// Retourne une copie du tableau <board> complété avec les positions
// prises sur la droite droite par une reine placée en <board(li,co)>
void prises_devant(int n, bool board[n][n], int li, int co) {
board[li][co] = false; // position de la reine
for (int j = 1; j < n-co; j++) {
// horizontale et diagonales à droite de la reine
if (j <= li) {
board[li-j][co+j] = false;
}
board[li][co+j] = false;
if (li+j < n) {
board[li+j][co+j] = false;
}
}
}
```
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment