Vous êtes sur la page 1sur 13

Problem Solving – Travaux dirigés n°1

Complexité & Optimalité Recherche du maximum


1. Concevez un algorithme de recherche du maximum dans un
ensemble à n éléments (vous disposez en tout et pour tout
d'une fonction de comparaison ≤).
Voici un algorithme de recherche du maximum dans un ensemble à n éléments, en
utilisant une fonction de comparaison ≤ :

1. Initialiser une variable max à l'élément le plus à gauche de l'ensemble.


2. Pour chaque élément x de l'ensemble, faire :
1. Si x > max, mettre à jour max en affectant la valeur de x à max.
3. Renvoyer max comme étant le maximum de l'ensemble.

Voici un exemple de code en Python qui implémente cet algorithme :

def trouver_maximum(ensemble, comparer):


max = ensemble[0]
for x in ensemble:
if comparer(x, max):
max = x
return max

Dans cet exemple, "ensemble" est un tableau d'éléments et "comparer" est une fonction qui prend
deux éléments en entrée et renvoie True si le premier est inférieur ou égal au second, et False sinon.
L'algorithme parcourt chaque élément de l'ensemble et met à jour la variable max si l'élément
courant est supérieur à max selon la fonction de comparaison. Une fois que tous les éléments de
l'ensemble ont été parcourus, l'algorithme renvoie max comme étant le maximum de l'ensemble.

2. Quelle est la complexité de votre algorithme en nombre de


comparaisons ?
La complexité de l'algorithme que j'ai proposé est de O(n), c'est-à-dire que le nombre
de comparaisons effectuées par l'algorithme est linéairement proportionnel à la taille de
l'ensemble (n).

En effet, l'algorithme parcourt chaque élément de l'ensemble une seule fois, et pour
chaque élément, une seule comparaison est effectuée. Ainsi, pour un ensemble de taille
n, l'algorithme effectuera au maximum n comparaisons.

La complexité de l'algorithme est donc O(n), ce qui signifie qu'elle est "linéaire" et
qu'elle ne devrait pas poser de problème de performance même pour des ensembles de
taille importante.

3. Montrez qu'il est optimal.


Pour montrer que l'algorithme de recherche du maximum proposé est optimal, il faut
prouver qu'il est impossible de trouver le maximum de l'ensemble en effectuant moins
de n comparaisons.

Pour cela, considérons un ensemble A de n éléments distincts. Dans le pire des cas, cet
ensemble sera trié de manière décroissante, c'est-à-dire que le premier élément sera le
plus grand et le dernier élément le plus petit.

Dans ce cas, il est impossible de trouver le maximum de l'ensemble en effectuant moins


de n comparaisons, car il faut au moins parcourir tous les éléments de l'ensemble pour
pouvoir comparer chaque élément avec le maximum courant. Ainsi, pour trouver le
maximum de l'ensemble, il faut au moins effectuer n comparaisons.

Par conséquent, l'algorithme de recherche du maximum proposé est optimal, car il ne


peut pas être amélioré en termes de nombre de comparaisons.
Recherche du deuxième plus grand élément
Nous supposerons ici que l'ensemble considéré ne contient pas
deux fois la même valeur.
1. Proposez un algorithme simple de recherche du deuxième
plus grand élément.
#include <stdio.h>

// Fonction qui retourne le deuxième plus grand élément de l'ensemble.


int trouver_deuxieme_plus_grand(int* ensemble, int n) {
// Initialiser le maximum et le deuxième plus grand élément.
int max = ensemble[0];
int deuxieme_plus_grand = ensemble[1];
if (deuxieme_plus_grand > max) {
// Si le deuxième élément est plus grand que le premier, inverser leurs valeurs.
int tmp = max;
max = deuxieme_plus_grand;
deuxieme_plus_grand = tmp;
}
// Parcourir le reste de l'ensemble.
for (int i = 2; i < n; i++) {
if (ensemble[i] > max) {
// Si l'élément courant est plus grand que le maximum, mettre à jour le
deuxième plus grand élément et le maximum.
deuxieme_plus_grand = max;
max = ensemble[i];
} else if (ensemble[i] > deuxieme_plus_grand) {
// Si l'élément courant est plus grand que le deuxième plus grand élément,
mais pas le maximum, mettre à jour le deuxième plus grand élément.
deuxieme_plus_grand = ensemble[i];
}
}
// Renvoyer le deuxième plus grand élément.
return deuxieme_plus_grand;
}

int main(int argc, char** argv) {


// Exemple d'utilisation de la fonction.
int ensemble[] = {1, 2, 3, 4, 5};
int n = sizeof(ensemble) / sizeof(int);
int deuxieme_plus_grand = trouver_deuxieme_plus_grand(ensemble, n);
printf("Le deuxième plus grand élément de l'ensemble est %d.\n",
deuxieme_plus_grand);
return 0;
}

Dans cet exemple, la fonction trouver_deuxieme_plus_grand prend en entrée un tableau


d'entiers ensemble et sa taille n, et retourne le deuxième plus grand élément de
l'ensemble.

L'algorithme commence par initialiser le maximum et le deuxième plus grand élément


avec les deux premiers éléments de l'ensemble. Si le deuxième élément est plus grand
que le premier, les valeurs sont inversées pour s'assurer que le maximum est bien le plus
grand élément
2. Quel est sa complexité en nombre de comparaisons ?
La complexité de l'algorithme de recherche du deuxième plus grand élément proposé
en C est de O(n), c'est-à-dire qu'elle est linéairement proportionnelle à la taille de
l'ensemble (n).

En effet, l'algorithme parcourt chaque élément de l'ensemble une seule fois et effectue
au plus deux comparaisons par élément. Ainsi, pour un ensemble de taille n,
l'algorithme effectuera au maximum 2n comparaisons.

La complexité de l'algorithme est donc O(n), ce qui signifie qu'elle est "linéaire" et
qu'elle ne devrait pas poser de problème de performance même pour des ensembles de
taille importante.
3. Récrivez votre algorithme de recherche du maximum sous la
forme d'un tournoi (de tennis, de foot, de pétanque ou de tout
autre sport). Il n'est pas nécessaire de formaliser l'algorithme
ici, une figure explicative sera amplement suffisante.
#include <stdio.h>

// Fonction qui retourne l'indice du maximum de l'ensemble.


int trouver_maximum(int* ensemble, int n, int (*comparer)(int, int)) {
// Si l'ensemble est vide, renvoyer -1.
if (n == 0) {
return -1;
}
// Si l'ensemble a un seul élément, renvoyer l'indice de cet élément.
if (n == 1) {
return 0;
}
// Initialiser l'indice du maximum à l'indice du premier élément.
int max = 0;
// Parcourir le reste de l'ensemble.
for (int i = 1; i < n; i++) {
// Si l'élément courant est plus grand que le maximum selon la fonction de
comparaison, mettre à jour l'indice du maximum.
if (comparer(ensemble[i], ensemble[max])) {
max = i;
}
}
// Renvoyer l'indice du maximum.
return max;
}

int main(int argc, char** argv) {


// Exemple d'utilisation de la fonction.
int ensemble[] = {1, 2, 3, 4, 5};
int n = sizeof(ensemble) / sizeof(int);
int max = trouver_maximum(ensemble, n, &comparer_inf_ou_egal);
printf("L'élément maximum de l'ensemble est %d.\n", ensemble[max]);
return 0;
}

Dans cet exemple, la fonction trouver_maximum prend en entrée un tableau d'entiers


ensemble , sa taille n, et une fonction de comparaison comparer qui prend deux entiers en
entrée et renvoie True si le premier est inférieur ou égal au second, et False sinon. La
fonction retourne l'indice du maximum de l'ensemble.
L'algorithme commence par vérifier si l'ensemble est vide ou s'il a un seul élément. Dans
ces cas, il renvoie -1 ou l'indice de cet élément, respectivement. Sinon, il initialise l'indice
du maximum à l'indice du premier élément et parcourt le reste de l'ensemble en
comparant chaque élément avec le maximum courant selon la fonction de comparaison.
Si un élément plus grand est trouvé, l'indice du maximum est mis à jour. Enfin, l'indice
du maximum est renvoyé.

4. Dans combien de comparaisons, le deuxième plus grand


élément de l'ensemble a-t-il été trouvé être le plus petit des
deux éléments comparés ?
Il est impossible de déterminer combien de comparaisons sont nécessaires pour trouver
le deuxième plus grand élément de l'ensemble en comparant deux éléments à la fois,
car cela dépend de la disposition des éléments de l'ensemble et de la fonction de
comparaison utilisée.

Par exemple, si l'ensemble est trié de manière croissante et que la fonction de


comparaison est l'opérateur inférieur ou égal, il sera nécessaire de parcourir l'ensemble
entier et de faire au moins n-1 comparaisons pour trouver le deuxième plus grand
élément.

En revanche, si l'ensemble est trié de manière décroissante et que la fonction de


comparaison est l'opérateur supérieur ou égal, il sera possible de trouver le deuxième
plus grand élément en comparant seulement deux éléments à la fois. Dans ce cas, il sera
nécessaire de faire au plus une comparaison pour trouver le deuxième plus grand
élément.

5. Proposez un nouvel algorithme de recherche du deuxième


plus grand élément.
Voici un nouvel algorithme de recherche du deuxième plus grand élément dans un
ensemble à n éléments, en utilisant une fonction de comparaison ≤ en C++ :

int findSecondLargest(int arr[], int n) {

int firstLargest = INT_MIN;


int secondLargest = INT_MIN;

for (int i = 0; i < n; i++) {

if (arr[i] > firstLargest) {

secondLargest = firstLargest;

firstLargest = arr[i];

} else if (arr[i] > secondLargest && arr[i] != firstLargest) {

secondLargest = arr[i];

return secondLargest;

6. Quelle est sa complexité en nombre de comparaisons ?


Cet algorithme parcourt le tableau et met à jour deux variables : firstLargest qui contient
le plus grand élément trouvé jusqu'à présent, et secondLargest qui contient le deuxième
plus grand élément trouvé jusqu'à présent. Si un élément du tableau est plus grand que
le plus grand élément trouvé jusqu'à présent, alors secondLargest est mis à jour avec la
valeur de firstLargest et firstLargest est mis à jour avec la valeur de l'élément du tableau. Si
un élément du tableau est plus grand que secondLargest mais pas plus grand que
firstLargest , alors secondLargest est mis à jour avec la valeur de l'élément du tableau.

Cet algorithme a une complexité en temps de O(n) car il parcourt le tableau entier une
seule fois.

Recherche du maximum et du minimum


Nous supposerons ici que l'ensemble considéré ne contient pas
deux fois la même valeur.
1. Proposez un algorithme naïf de recherche du maximum et du
minimum d'un ensemble de n éléments.
Voici un algorithme naïf en C qui permet de trouver le maximum et le minimum d'un
ensemble de n éléments :

void findMinMax(int arr[], int n, int *min, int *max) {


*min = INT_MAX;
*max = INT_MIN;

for (int i = 0; i < n; i++) {


if (arr[i] < *min) {
*min = arr[i];
}
if (arr[i] > *max) {
*max = arr[i];
}
}
}

Cet algorithme parcourt le tableau et met à jour deux variables, min et max, qui
contiennent respectivement le minimum et le maximum trouvés jusqu'à présent. Si un
élément du tableau est plus petit que le minimum trouvé jusqu'à présent, alors min est
mis à jour avec la valeur de l'élément du tableau. Si un élément du tableau est plus
grand que le maximum trouvé jusqu'à présent, alors max est mis à jour avec la valeur de
l'élément du tableau.
Cet algorithme a une complexité en temps de O(n) car il parcourt le tableau entier une
seule fois.

2. Quelle est sa complexité en nombre de comparaisons ?


Il existe cependant des algorithmes qui permettent de trouver le minimum et le maximum d'un
tableau avec une complexité en nombre de comparaisons inférieure à O(n). Par exemple,
l'algorithme de recherche du minimum et du maximum en utilisant une comparaison à trois
éléments permet de trouver le minimum et le maximum d'un tableau en effectuant au plus n/2
comparaisons (où n est la taille du tableau). Cet algorithme est plus rapide que l'algorithme naïf
que j'ai proposé, mais il est également plus complexe à implémenter.

3. Proposez un algorithme plus efficace. Indication : dans une


première phase les éléments sont comparés par paire.
void findMinMax(int arr[], int n, int *min, int *max) {
int i = 0;

/* Si le tableau contient un nombre impair d'éléments, alors le premier élément


est comparé au minimum et au maximum */
if (n % 2 == 1) {
*min = *max = arr[0];
i = 1;
}

/* Sinon, les deux premiers éléments sont comparés et le minimum et le


maximum sont mis à jour en fonction du résultat de la comparaison */
else {
if (arr[0] > arr[1]) {
*max = arr[0];
*min = arr[1];
}
else {
*max = arr[1];
*min = arr[0];
}
i = 2;
}

/* Les éléments restants du tableau sont comparés par paire */


while (i < n - 1) {
if (arr[i] > arr[i+1]) {
if (arr[i] > *max) {
*max = arr[i];
}
if (arr[i+1] < *min) {
*min = arr[i+1];
}
}
else {
if (arr[i+1] > *max) {
*max = arr[i+1];
}
if (arr[i] < *min) {
*min = arr[i];
}
}
i += 2;
}
}

4. Quelle est sa complexité en nombre de comparaisons ?


Cet algorithme parcourt le tableau et compare les éléments par paire. Si le tableau
contient un nombre impair d'éléments, alors le premier élément est comparé au
minimum et au maximum. Sinon, les deux premiers éléments sont comparés et le
minimum et le maximum sont mis à jour en fonction du résultat de la comparaison.
Ensuite, les éléments restants du tableau sont comparés par paire et le minimum et le
maximum sont mis à jour si nécessaire.

Cet algorithme a une complexité en temps de O(n) car il parcourt le tableau entier une
seule fois. Sa complexité en nombre de comparaisons est inférieure à O(n), car le
nombre de comparaisons effectuées est au plus n/2 (pour un tableau de taille n).

5. Montrez que cet algorithme est optimal. Indication : on


appelle unité d'information :
l'information < l'élément x ne peut pas être le plus grand
élément >,
l'information < l'élément x ne peut pas être le plus petit
élément >.
(a) Quel est le nombre minimal d'unités d'information qu'un
algorithme de recherche du maximum et du minimum doit
produire pour nous garantir la validité de son résultat ?
(b) Combien d'unités d'information sont produites par la
comparaison de deux éléments (distinguez des cas, suivant que
l'on a ou non des unités d'informations sur ces valeurs).
(c) Concluez.

Vous aimerez peut-être aussi