Un algorithme glouton construit une solution étape par étape en faisant, à chaque étape, le choix qui paraît le meilleur localement, sans jamais revenir en arrière.
Objectif : rendre une somme $S$ avec le minimum de pièces, à partir d'un système de pièces donné.
Stratégie gloutonne : à chaque étape, choisir la plus grande pièce inférieure ou égale au montant restant.
Exemple : rendre 37 centimes avec les pièces ${1,\; 2,\; 5,\; 10,\; 20,\; 50}$ :
Résultat : quatre pièces. Avec le système euro, l'algorithme glouton donne toujours l'optimum.
Avec un système de pièces non standard, l'algorithme glouton peut échouer.
Exemple : pièces ${1,\; 3,\; 4}$, rendre 6.
Le glouton a choisi la plus grande pièce (4) alors qu'il fallait prendre deux pièces de 3. Il faut dans ce cas utiliser la programmation dynamique.
Objectif : remplir un sac de capacité $C$ avec des objets ayant chacun un poids et une valeur, en maximisant la valeur totale. On peut prendre une fraction d'un objet.
Stratégie gloutonne : trier les objets par rapport valeur/poids décroissant, puis prendre les objets dans cet ordre tant que le sac n'est pas plein.
Cette stratégie donne l'optimum uniquement dans la version fractionnaire. Dans la version 0/1 (on prend l'objet en entier ou pas du tout), le glouton ne garantit pas l'optimum.
Objectif : choisir le maximum d'activités compatibles (qui ne se chevauchent pas) parmi un ensemble d'activités ayant chacune une heure de début et une heure de fin.
Stratégie gloutonne : trier les activités par heure de fin croissante, puis sélectionner chaque activité dont l'heure de début est postérieure ou égale à la fin de la dernière activité sélectionnée.
Ce glouton donne toujours l'optimum (prouvable par échange).
| Erreur | Correction | Explication |
|---|---|---|
| Trier par valeur décroissante pour le sac à dos | Trier par rapport valeur/poids décroissant | Un objet lourd et cher peut être moins rentable qu'un objet léger et bon marché. |
| Ne pas vérifier que la pièce est $\leqslant$ au reste | Ajouter if pieces[i] <= reste | Sans cette vérification, on tente de rendre plus que le montant restant. |
| Utiliser le glouton avec les pièces ${1, 3, 4}$ pour rendre 6 | Utiliser la programmation dynamique | Le glouton donne trois pièces ($4+1+1$) au lieu de deux ($3+3$). |
| Trier les activités par durée | Trier par heure de fin | Trier par durée ne donne pas l'optimum : une activité courte peut chevaucher plusieurs autres. |
def rendu_monnaie(montant, pieces):
"""Renvoie la liste des pièces utilisées (stratégie gloutonne)."""
pieces_triees = sorted(pieces, reverse=True)
resultat = []
reste = montant
for p in pieces_triees:
while reste >= p:
resultat.append(p)
reste -= p
return resultat
# Système euro
print(rendu_monnaie(37, [1, 2, 5, 10, 20, 50]))
# [20, 10, 5, 2]
# Système pathologique
print(rendu_monnaie(6, [1, 3, 4]))
# [4, 1, 1] -> 3 pièces (non optimal : 3+3 = 2 pièces)
def activites_max(activites):
"""Renvoie le nombre maximum d'activités compatibles.
activites : liste de tuples (debut, fin)."""
# Trier par heure de fin croissante
triees = sorted(activites, key=lambda a: a[1])
selection = [triees[0]]
for i in range(1, len(triees)):
if triees[i][0] >= selection[-1][1]:
selection.append(triees[i])
return selection
activites = [(1, 3), (2, 5), (3, 6), (5, 7), (6, 8), (8, 10)]
print(activites_max(activites))
# [(1, 3), (5, 7), (8, 10)] -> 3 activités
def sac_a_dos_fractionnaire(capacite, objets):
"""objets : liste de tuples (poids, valeur).
Renvoie la valeur maximale (on peut couper les objets)."""
# Trier par rapport valeur/poids décroissant
tries = sorted(objets, key=lambda o: o[1]/o[0], reverse=True)
valeur_totale = 0
reste = capacite
choix = []
for poids, valeur in tries:
if reste >= poids:
choix.append((poids, valeur, 1.0))
valeur_totale += valeur
reste -= poids
elif reste > 0:
fraction = reste / poids
choix.append((poids, valeur, fraction))
valeur_totale += valeur * fraction
reste = 0
return valeur_totale, choix
objets = [(10, 60), (20, 100), (30, 120)]
val, detail = sac_a_dos_fractionnaire(50, objets)
print(f"Valeur maximale : {val}") # 240.0
rendu_monnaie(montant, pieces) qui implémente l'algorithme glouton et renvoie la liste des pièces utilisées.Glouton pour 63 avec ${1, 2, 5, 10, 20, 50}$ :
Résultat : quatre pièces ${50, 10, 2, 1}$. C'est l'optimum pour le système euro.
Glouton pour 14 avec ${1, 7, 10}$ :
Résultat glouton : cinq pièces ${10, 1, 1, 1, 1}$. Or $7 + 7 = 14$ ne nécessite que deux pièces. Le glouton ne donne pas l'optimum ici.
Implémentation :
def rendu_monnaie(montant, pieces):
pieces_triees = sorted(pieces, reverse=True)
resultat = []
reste = montant
for p in pieces_triees:
while reste >= p:
resultat.append(p)
reste -= p
return resultat
Un élève souhaite participer au maximum d'activités dans la journée. Voici les créneaux proposés :
| Activité | Début | Fin |
|---|---|---|
| Piscine | 8h | 10h |
| Escalade | 9h | 11h |
| Tennis | 10h | 12h |
| Cinéma | 11h | 14h |
| Vélo | 13h | 15h |
| Lecture | 14h | 16h |
Activités triées par heure de fin : Piscine (8-10), Escalade (9-11), Tennis (10-12), Cinéma (11-14), Vélo (13-15), Lecture (14-16).
Application du glouton (on compare toujours avec la dernière activité sélectionnée) :
Résultat : Piscine, Tennis, Vélo (trois activités).
Implémentation :
def activites_max(activites):
triees = sorted(activites, key=lambda a: a[1])
selection = [triees[0]]
for i in range(1, len(triees)):
if triees[i][0] >= selection[-1][1]:
selection.append(triees[i])
return selection
activites = [(8, 10), (9, 11), (10, 12), (11, 14), (13, 15), (14, 16)]
print(activites_max(activites))
# [(8, 10), (10, 12), (13, 15)]
Un randonneur a un sac de capacité 15 kg. Il dispose des objets suivants :
| Objet | Poids (kg) | Valeur | Rapport valeur/poids |
|---|---|---|---|
| A | 5 | 30 | ? |
| B | 10 | 50 | ? |
| C | 8 | 40 | ? |
| D | 3 | 24 | ? |
Application du glouton (capacité 15 kg) :
Valeur maximale : 89.
Implémentation :
def sac_a_dos_fractionnaire(capacite, objets):
tries = sorted(objets, key=lambda o: o[1]/o[0], reverse=True)
valeur_totale = 0
reste = capacite
for poids, valeur in tries:
if reste >= poids:
valeur_totale += valeur
reste -= poids
elif reste > 0:
valeur_totale += valeur * (reste / poids)
reste = 0
return valeur_totale
objets = [(5, 30), (10, 50), (8, 40), (3, 24)]
print(sac_a_dos_fractionnaire(15, objets)) # 89.0