2022-05-04 18:39:31 +02:00
> En informatique, on retrouve fréquemment des problèmes d'optimisation comme par exemple le problème du **rendu de monnaie**. Les algorithmes gloutons sont utilisés pour résoudre ce type de problème.
2022-07-21 15:38:24 +02:00
## Le Programme
2022-05-04 18:39:31 +02:00

--------
### Définition
**Problème d'optimisation** : problème dont on cherche à maximiser (ou minimiser) un résultat.
Un ** *algorithme glouton*** est un algorithme qui vise à optimiser la résolution d'un problème en utilisant une approche particulière :
- On procède par étapes, en effectuant à chaque fois le meilleur choix possible.
- On ne revient jamais sur un choix déjà fait.
2024-03-23 19:42:49 +01:00
- Répétez ces étapes jusqu'à ce qu'une solution globale soit obtenue.
2022-05-04 18:39:31 +02:00
Selon le problème on utilise une méthode pour résoudre ce dernier. Il peut exister plusieurs instances qui donneront lieu à des résultats différents.
À un problème d'optimisation, on associe une fonction objective.
2026-01-15 18:32:27 +01:00
Prenons un exemple simple : la liste des 23 joueurs sélectionnés pour la coupe du monde :
2022-05-04 18:39:31 +02:00
```
2024-03-23 19:42:49 +01:00
Didier Desvilles est bien embêté : il doit choisir 23 joueurs pour aller jouer une compétition.
2022-05-04 18:39:31 +02:00
Dans cette optique, il décide d'utiliser une méthode simple: choisir le meilleur joueur possible pour chaque poste, puis le deuxième meilleur comme remplaçant.
2026-01-15 18:32:27 +01:00
Peu importe si les joueurs sélectionnés ne s'entendent pas forcément très bien, comme Olivier Gibrun et Karim Benzemb, l'important c'est de faire le meilleur choix à chaque étape.
2022-05-04 18:39:31 +02:00
```
2026-01-15 18:32:27 +01:00
Un algorithme glouton ne renvoie pas obligatoirement un résultat optimal, dans ce cas-là, on parle ** *d'heuristique.*** Il renverra un résultat optimal pour chacun des sous-problèmes.
2022-05-04 18:39:31 +02:00
Un algorithme glouton ne revient pas sur un sous-problème déjà traité.
---------
### Exemple de problème d'optimisation : rendu de monnaie
> Le problème du rendu de monnaie s'énonce de la façon suivante : étant donné un système de monnaie (pièces et billets), comment rendre une somme donnée de façon optimale, c'est-à-dire avec le nombre minimal de pièces et billets ?
2026-01-15 18:32:27 +01:00
On veut programmer une caisse automatique qui rend de façon optimale la monnaie (le moins de pièces/billets) :
2022-05-04 18:39:31 +02:00
#### **Des exemples**
2026-01-15 18:32:27 +01:00
**Exemple 1 :**
- Système de monnaie : `{1, 2, 5, 10}`
- Recherché : `14`
- Résultat optimal : `10 + 2 + 2` (3 pièces)
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
**Exemple 2 :**
- Système de monnaie : `{1, 2, 5, 7, 10}`
- Recherché : `14`
- Résultat optimal : `7 + 7` (2 pièces)
2022-05-04 18:39:31 +02:00
#### **Algorithme**
```python
2024-03-21 14:53:09 +01:00
def rendu_monnaie(systeme, montant):
reste = montant
i = len(systeme) - 1
resultat = []
while reste > 0 and i >= 0:
# On trouve le nombre de pièces de la valeur courante à rendre
2024-03-23 19:42:49 +01:00
nb_pieces = reste // systeme[i]
for _ in range(nb_pieces): # Pour chaque pièce à rendre
resultat.append(systeme[i]) # On ajoute cette pièce au résultat
reste -= systeme[i] # On met à jour le reste à rendre
i -= 1 # On passe à la valeur inférieure dans le système
2024-03-21 14:53:09 +01:00
return resultat
# Exemples d'utilisation
systeme_1 = [1, 2, 5, 10]
systeme_2 = [1, 2, 5, 7, 10]
print(rendu_monnaie(systeme_1, 14)) # Devrait afficher [10, 2, 2] qui est optimal pour le système_1
print(rendu_monnaie(systeme_2, 14)) # Devrait afficher [10, 2, 2] qui est non optimal car [7, 7] est mieux pour le système_2
2024-03-23 19:42:49 +01:00
2022-05-04 18:39:31 +02:00
```
2026-01-15 18:32:27 +01:00
Force brute avec le ```systeme_2` `` pour ` ``14` ``:
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
On cherche **toutes** les solutions possibles :
2022-05-04 18:39:31 +02:00
- \[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\]
- \[2, 2, 2, 2, 2, 2, 2\]
- \[5, 5, 2, 2\]
- \[7, 7\]
- \[10, 1, 1, 1, 1\]
- \[10, 2, 2]
- ...
2026-01-15 18:32:27 +01:00
On parcourt la liste des solutions pour garder la solution la plus optimale (la plus courte) : ** \[7, 7\]**.
2022-05-04 18:39:31 +02:00
### Exemples de problèmes que l'on peut résoudre par algorithme glouton
- **Sac à dos**
- **Rendu de monnaie**
- [Gestion d'organisation de classe ](https://info.blaisepascal.fr/nsi-algorithmes-gloutons#Un_algorithme_glouton-2 )
- **Voyageur de commerce**.
### **Sac à dos**
2026-01-15 18:32:27 +01:00
> Un voleur entre dans une maison avec un sac à dos pouvant contenir un poids maximum. Il trouve plusieurs objets, chacun ayant une valeur et un poids. Quels objets doit-il prendre pour maximiser la valeur totale sans dépasser la capacité du sac ?
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
#### Exemple
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
Objets disponibles (valeur, poids) : `[(60, 10), (100, 20), (120, 30)]`
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
Capacité du sac : `50 kg`
#### Stratégies gloutonnes possibles
1. **Prendre l'objet de plus grande valeur** en premier
2. **Prendre l'objet le plus léger** en premier
3. **Prendre l'objet avec le meilleur ratio valeur/poids** en premier (souvent la meilleure stratégie)
#### Algorithme (stratégie ratio valeur/poids)
```python
def sac_a_dos_glouton(objets, capacite):
"""
objets : liste de tuples (valeur, poids)
capacite : poids maximum du sac
"""
# Calculer le ratio valeur/poids et trier par ratio décroissant
objets_avec_ratio = [(v, p, v/p) for v, p in objets]
objets_tries = sorted(objets_avec_ratio, key=lambda x: x[2], reverse=True)
poids_total = 0
valeur_totale = 0
sac = []
for valeur, poids, ratio in objets_tries:
if poids_total + poids < = capacite:
sac.append((valeur, poids))
poids_total += poids
valeur_totale += valeur
return sac, valeur_totale, poids_total
# Exemple d'utilisation
objets = [(60, 10), (100, 20), (120, 30)]
capacite = 50
sac, valeur, poids = sac_a_dos_glouton(objets, capacite)
2026-01-29 23:37:26 +01:00
print("Objets pris :", sac)
print("Valeur totale :", valeur, ", Poids total :", poids)
2026-01-15 18:32:27 +01:00
# Résultat : [(60, 10), (100, 20)] avec valeur=160 et poids=30
# Note : ce n'est pas optimal ! L'optimal serait [(100, 20), (120, 30)] = 220
```
2022-05-04 18:39:31 +02:00
2026-01-15 18:32:27 +01:00
> **Attention** : L'algorithme glouton ne donne pas toujours la solution optimale pour le problème du sac à dos !
---------
2022-05-04 18:39:31 +02:00
### **Voyageur de commerce**
2026-01-15 18:32:27 +01:00
> Un voyageur de commerce doit visiter plusieurs villes exactement une fois, puis revenir à son point de départ. Quel est le chemin le plus court ?
#### Exemple
Imaginons 4 villes : A, B, C, D avec les distances suivantes :
| De/Vers | A | B | C | D |
|---------|---|---|---|---|
| A | - | 10 | 15 | 20 |
| B | 10 | - | 35 | 25 |
| C | 15 | 35 | - | 30 |
| D | 20 | 25 | 30 | - |
#### Stratégie gloutonne : "Plus proche voisin"
À chaque étape, on se déplace vers la ville non visitée la plus proche.
```python
def voyageur_commerce_glouton(distances, depart=0):
"""
distances : matrice des distances entre villes
depart : indice de la ville de départ
"""
n = len(distances)
visitees = [False] * n
chemin = [depart]
visitees[depart] = True
distance_totale = 0
ville_actuelle = depart
for _ in range(n - 1):
# Trouver la ville non visitée la plus proche
plus_proche = None
dist_min = float('inf')
for ville in range(n):
if not visitees[ville] and distances[ville_actuelle][ville] < dist_min:
dist_min = distances[ville_actuelle][ville]
plus_proche = ville
# Se déplacer vers cette ville
chemin.append(plus_proche)
visitees[plus_proche] = True
distance_totale += dist_min
ville_actuelle = plus_proche
# Retour au point de départ
distance_totale += distances[ville_actuelle][depart]
chemin.append(depart)
return chemin, distance_totale
# Exemple d'utilisation
distances = [
[0, 10, 15, 20], # Distances depuis A
[10, 0, 35, 25], # Distances depuis B
[15, 35, 0, 30], # Distances depuis C
[20, 25, 30, 0] # Distances depuis D
]
chemin, distance = voyageur_commerce_glouton(distances, depart=0)
villes = ['A', 'B', 'C', 'D']
chemin_noms = [villes[i] for i in chemin]
2026-01-29 23:37:26 +01:00
print("Chemin :", ' -> '.join(chemin_noms))
print("Distance totale :", distance)
2026-01-15 18:32:27 +01:00
# Résultat : A -> B -> D -> C -> A avec distance = 80
```
> **Note** : Le problème du voyageur de commerce est un problème **NP-difficile**. Pour un grand nombre de villes, trouver la solution optimale prend un temps exponentiel. L'approche gloutonne donne une solution acceptable rapidement, mais rarement optimale.
2022-05-04 18:39:31 +02:00
2022-05-06 10:32:24 +02:00
--------
Auteur : Florian Mathieu
Licence CC BY NC
< a rel = "license" href = "http://creativecommons.org/licenses/by-nc-sa/4.0/" > < img alt = "Licence Creative Commons" style = "border-width:0" src = "https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" / > < / a > < br / > Ce cours est mis à disposition selon les termes de la < a rel = "license" href = "http://creativecommons.org/licenses/by-nc-sa/4.0/" > Licence Creative Commons Attribution - Pas d’ Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International< / a >