Résoudre des problèmes d'optimisation, un choix local à la fois — CPGE MP/PSI
1 / 13
Problèmes d'optimisationIntro
Trouver, parmi un très grand nombre de solutions, celle qui minimise ou maximise une fonction objectif.
Rendu de monnaie → le moins de pièces.
Sac à dos → la plus grande valeur transportée.
Compression → le moins de bits.
Force brute : explorer toutes les combinaisons coûte un temps exponentiel — irréaliste.
2 / 13
L'idée gloutonnePrincipe
Construire la solution pas à pas. À chaque étape, faire le choix localement optimal (le plus « gourmand »), sans jamais revenir en arrière.
defglouton(candidats):
solution = []
while not complet(solution) and candidats:
x = meilleur_candidat(candidats) # choix glouton
candidats.remove(x)
if compatible(solution, x):
solution.append(x)
return solution
Rapide et simple — mais « localement » ne veut pas dire « globalement » optimal.
3 / 13
Les deux ingrédientsThéorie
1. Propriété du choix glouton. Il existe une solution optimale contenant le choix localement optimal de la première étape : ce choix ne ferme jamais la porte à l'optimum.
2. Sous-structure optimale. Une solution optimale contient des solutions optimales des sous-problèmes : après un choix, il reste un problème de même nature, plus petit.
Si l'une manque → le glouton peut échouer.
4 / 13
Glouton vs programmation dynamiqueComparaison
Glouton
Programmation dynamique
Un seul choix par étape
Plusieurs choix examinés
Jamais de retour en arrière
Combine les sous-problèmes
Très rapide
Plus coûteuse
Correct si choix glouton vérifié
Toujours optimale
5 / 13
Rendu de monnaieExemple 1
Rendre une somme $S$ avec le moins de pièces. Glouton : prendre la plus grande pièce $\le$ reste.
defrendu_monnaie(systeme, S):
rendu = []
for piece in systeme: # décroissantwhile S >= piece:
rendu.append(piece)
S = S - piece
return rendu
68 = 50 + 10 + 5 + 2 + 1 → 5 pièces (optimal).
6 / 13
Rendu : ça dépend du système !Contre-exemple
Système euro (canonique). Le glouton donne toujours le nombre minimal de pièces.
Glouton. Tant qu'il reste > 1 arbre : fusionner les deux plus petites fréquences sous une racine de poids = leur somme.
Forêt initiale : un arbre par caractère, pondéré par sa fréquence.
10 / 13
Huffman : implémentationCode
import heapq
defhuffman(freq):
tas = [[f, c, None, None] for c, f in freq.items()]
heapq.heapify(tas)
whilelen(tas) > 1:
g = heapq.heappop(tas) # 2 plus petites
d = heapq.heappop(tas)
heapq.heappush(tas, [g[0]+d[0], None, g, d])
return tas[0]