Académique Documents
Professionnel Documents
Culture Documents
Exo01*:
Trouver "à la main" la meilleure des solutions pour le problème ci-dessus.
Comparer vos solutions pour savoir si vous avez bien la meilleure des solutions et s'il en existe plusieurs.
2) Algorithme permettant de trouver la solution optimale
Il existe différentes méthodes algorithmiques permettant de trouver une solution optimale à un
problème d'optimisation : il peut, en effet, être intéressant "d'automatiser" la résolution des
problèmes d'optimisation à l'aide d'algorithme (dans notre cas, trouver un algorithme qui trouve
une solution optimale au problème du sac à dos).
En apparence, la solution la plus simple dans le cas du sac à dos serait d'écrire un algorithme qui
teste toutes les combinaisons d'objets possibles et qui retient les solutions qui offrent un gain
maximum. Dans notre cas précis, avec seulement 4 objets, cette solution pourrait être envisagée,
mais avec un plus grand nombre d'objets, le temps de calculs, même pour un ordinateur très
puissant, deviendrait trop important. En effet l'algorithme qui testerait toutes les combinaisons
possibles aurait une complexité en temps en O(a n) avec a une constante et n le nombre d'objets.
On parle d'une complexité exponentielle. Les algorithmes à complexité exponentielle ne sont pas
efficaces pour résoudre des problèmes, le temps de calcul devient beaucoup trop important quand
n devient très grand.
3) Algorithme glouton (ou méthode gloutonne) :
Nous avons une contrainte de masse avec un gain maximal à trouver.
a) Méthode gloutonne appliquée à des objets classés par C B D A
masse décroissante
12 kg 11 kg 8 kg 6 kg
À chaque étape, on sélectionne la masse la plus importante 300 € 385 € 320 € 220 €
possible et on calcule le gain.
1ère étape : C (12 kg) : il reste 35–12 = 23 kg et le gain est de 300 €
2ème étape : C (12 kg) : il reste 23–12 = 11 kg et le gain est de 300+300 = 600 €
3ème étape : B (11 kg) : il reste 11–11 = 0 kg et le gain est de 600+385 = 985 €
Résultat : le sac sera composé de 2 objets C et 1 objet B pour un gain de 985 €
Nous avons trouvé une solution mais est-elle optimale ? Correspond-t-elle à la solution
empirique trouvée "à la main"
B C D A
b) Méthode gloutonne appliquée à des objets classés par
11 kg 12 kg 8 kg 6 kg
valeur décroissante
385 € 300 € 320 € 220 €
A chaque étape, on sélectionne la valeur la plus importante
possible si la masse le permet et on calcule le gain.
1ère étape : B (11 kg) : il reste 35–11 = 24 kg et le gain est de 385 €
2ème étape : B (11 kg) : il reste 24–11 = 13 kg et le gain est de 385+385 = 770 €
3ème étape : B (11 kg) : il reste 13–11 = 2 kg et le gain est de 770+385 = 1155 €
Résultat : le sac sera composé de 3 objets B pour un gain de 1155 €
Nous avons trouvé une meilleure solution que précédemment mais est-elle optimale ?
c) Méthode gloutonne appliquée à des objets D A B C
classés par valeur massique décroissante (la 8 kg 6 kg 11 kg 12 kg
valeur massique est la valeur par kg)
320 € 220 € 385 € 300 €
A chaque étape, on sélectionne la valeur 320/8 = 220/6 = 385/11 = 300/12 =
massique la plus importante possible si la masse 40,0 €/kg 36,7 €/kg 35,0 €/kg 25,0 €/kg
le permet et on calcule le gain.
1ère étape : D (8 kg) : il reste 35–8 = 27 kg et le gain est de 320 €
2ème étape : D (8 kg) : il reste 27–8 = 19 kg et le gain est de 320+320 = 640 €
3ème étape : D (8 kg) : il reste 19–8 = 11 kg et le gain est de 640+320 = 960 €
4ème étape : D (8 kg) : il reste 11–8 = 3 kg et le gain est de 960+320 = 1280 €
Résultat : le sac sera composé de 4 objets D pour un gain de 1280 €
Nous avons trouvé une meilleure solution que les 2 précédentes. On pourrait penser qu'il s'agit
de la solution optimale … mais ce n'est pas vrai ! En effet, si je prends 3 objets D (3*8 = 24kg et
3*320 = 960 €) et un objet B : (1*11 = 11 kg et 1*385 = 385 €), j'obtiens bien 24+11 = 35 kg mais
la somme est de 960 + 385 = 1345 €……
La méthode gloutonne vue est toujours la même mais nous l'appliquons sur des objets classés
différemment et cet ordre de départ donne des résultats différents.
d) Conclusion
Plus généralement, il est important de bien comprendre qu'un algorithme glouton ne donne pas
forcement une solution optimale : il donne une solution approximative. Néanmoins, pour certains
types de problèmes, il est possible de montrer qu'un algorithme glouton donnera toujours une
solution optimale (la preuve dépasse largement le cadre de ce cours … et de mes compétences
mathématiques et informatiques !).
Exo02*
Écrire l'algorithme de la méthode gloutonne (c'est le même algorithme appliqué à 3 tableaux classés différemment) et
l'implémenter en PYTHON avec les valeurs données.
Écrire 3 fonctions différentes qui a en paramètre un tableau dont chaque élément est un tuple formé du
nom, de la masse et de la valeur de l'objet et qui renvoie un tableau rangé par :
Masse décroissante
Valeur décroissante
Valeur massique décroissante
Exemple : tableau d'objets A, B, C et D : [(A, 9, 9), (B, 3, 4), (C, 5, 2), (D, 8,14)]
Classé par masse décroissante : [(A, 9, 9), (D, 8,14), (C, 5, 2), (B, 3, 4)]
Classé par valeur décroissante : [(D, 8,14), (A, 9, 9), (B, 3, 4), (C, 5, 2)]
Classé par valeur massique décroissante : [(D, 8,14), (B, 3, 4), (A, 9, 9), (C, 5, 2)]
Trouver la meilleure solution pour un sac de 19 kg, pour un sac de 25 kg et pour un sac de 45 kg avec
objets A B C D E F G H I J K L M N
Masse (en kg) 6 3 5 8 7 4 20 6 2 4 4 10 2 10
Valeurs (en €) 9 3 8 14 10 7 20 7 3 3 6 12 2 4
Exo03*
Écrire l'algorithme glouton du rendu de monnaie.
L'algorithme a au moins 2 variables : le prix à payer et la somme remise par le client.
Il doit avoir en sortie les billets et pièces à rendre afin qu'il y ait le moins possible de pièces et billets.
2) Fonction en Python
Exo04*
Implémentez votre algorithme dans une fonction permettant le rendu de monnaie.
La fonction doit calculer la somme à rendre à un client et indiquer les billets et pièces à rendre afin qu'il y ait le moins
possible de pièces et billets.
La fonction :
a 2 paramètres en entrée :
Un prix à payer (un ticket de caisse par exemple)
Une somme d'argent liquide donné pour payer le prix
renvoie 2 données :
La somme d'argent à rendre au client
Un tableau dont chaque élément est un tableau ou un tuple ou un dictionnaire contenant la valeur des
billets et pièces et le nombre de ces derniers à rendre.
Les pièces sont : 0,01 € ; 0,02 € ; 0,05 € ; 0,10 € ; 0,20 € ; 0,50 € ; 1 € ; 2 €
Les billets sont : 5 € ; 10 € ; 20 € ; 50 € ; 100 € ; 200 € ; 500 €
Exemple : prix à payer 11,24 € Argent donné 20 € Il faut donc rendre 20 – 11,24 = 8,76 €
Vous devez obtenir :
3 pièces de 0,02 € + 1 pièce de 0,20 € + 1 pièce de 0,50 € + 1 pièce de 1 € + 1 pièce de 2€ + 1 billet de 5€
Vous ne devez pas obtenir :
76 pièces de 0,01 € et 8 pièces de 1 € par exemple ………
Conseils :
Attention aux variables locales et globales
N'utiliser que des instructions simples (données dans l'aide-mémoire)
Votre fonction doit vérifier si la somme donnée est bien supérieure au prix à payer
Corrigé ALG 07 Algorithme glouton
Exo02 problème du sac à dos
Mercredi 26/05, avec le groupe présent, nous avons établi l'algorithme suivant :
fonction gloutonne (tab, masseSac) :
# tab est un tableau classé dans l'ordre désiré contenant des tuples (nom, masse, valeur, valeur massique)
gain = 0 # variable correspondant au gain obtenu avec tous les objets "volés"
liste = [] # variable correspondant au tableau contenant tous les objets "volés"
i=0
tant que masseSac ≥ tab[–1][1] # tab[–1][1] désigne la valeur de la masse du dernier objet dans tab
si tab[i][1] ≤ masseSac
masseSac = masseSac – tab[i][1]
#on enlève la masse du sac de tab[i] à la masse restante du sac
gain = gain + tab[i][2] #on ajoute la valeur de tab[i] au gain obtenu
ajouter tab[i][0] à la variable liste
#on ajoute le nom de tab[i] au tableau contenant les noms des objets "volés"
sinon i = i + 1
renvoyer gain, liste, masseSac
Cette fonction est testée avec les 3 cas vus dans le cours :
Objets classés par masse décroissante :
gloutonne([("C",12,300),("B",11,385),("D",8,320),("A",6,220)],35)
renvoie (985, ['C', 'C', 'B'], 0)
Cela correspond à ce qui était prévu
Objets classés par valeur décroissante :
gloutonne([("B",11,385),("C",12,300),("D",8,320),("A",6,220)],35)
renvoie (1155, ['B', 'B', 'B'], 2)
Cela correspond à ce qui était prévu
Objets classés par valeur massique décroissante :
gloutonne([("D",8,320),("A",6,220),("B",11,385),("C",12,300)],35)
renvoie (960, ['D', 'D', 'D'], 11)
Cela ne correspond pas à ce qui était prévu : nous aurions dû trouver (1280, ['D', 'D', 'D',
'D'], 3)
À la fin de l'heure de mercredi, un de vos camarades (Maxime) a trouvé ce problème : l'algorithme n'est pas bon…
Le problème vient de la condition dans le "tant que" : mettre une condition sur la masse du dernier du tableau n'est
pas valable tout le temps. En effet, la masse la plus faible de tous les objets n'est pas forcément placée à la fin du
tableau ‼!
Il y a donc 2 possibilités :
Soit avant la boucle, on cherche à déterminer la masse la plus faible de tous les objets mais cela nécessite de
faire une boucle passant en revue tous les objets de la liste (donc la durée d'exécution sera plus longue si le tableau est grand)
Soit une change la condition : elle doit porter sur la masse du sac qui doit rester positive et sur le fait que le
nombre d'objets dans la liste ne doit pas être dépassé (ainsi la condition ne porte pas sur une valeur contenu
dans le tableau). J'ai proposé à Maxime la modification suivante du "tant que" :
tant que masseSac > 0 ET i < longueur du tableau
La fonction devient :
def gloutonne(tab, masseSac) :
gain = 0
liste = []
i = 0
while masseSac > 0 and i < len(tab) :
if tab[i][1] <= masseSac :
masseSac = masseSac – tab[i][1]
gain = gain + tab[i][2]
liste.append(tab[i][0])
else :
i = i + 1
return gain, liste, masseSac
Je vous propose aussi une autre possibilité d'algorithme (et donc de fonction en Python) pour éviter de faire autant de
boucle avec quotient entier de la division ( // en Python) et le reste entier de la division (opérateur % en Python) :
fonction gloutonne (tab, masseSac) :
gain = 0
liste = []
i = 0
tant que masseSac > 0 ET i < longueur du tableau
nbObjets = quotient de la division (masseSac/masse de tab[i])
# on calcule commet de fois la masse de l'objet classé à la position i peut entrer dans la sac
masseSac = reste de la division (masseSac/tab[i][1])
#on calcule la masse restante du sac
gain = gain + nbObjets*tab[i][2] #on ajoute la valeur de tab[i] au gain obtenu
pour j allant de 0 à nbObjets
ajouter tab[i][0] à la variable liste
i = i+1
renvoyer gain, liste, masseSac
Ensuite le problème à résoudre est de faire 3 algorithmes et donc 3 fonctions permettant de classer un tableau non
classé contenant des tuples (nom, masse, valeur) en
Un tableau classé par masse décroissante (et en cas d'égalité des masses, par valeur de l'objet décroissante)
Un tableau classé par valeur de l'objet décroissante (et en cas d'égalité des valeurs, par masse croissante)
Un tableau classé par valeur massique décroissante (et en cas d'égalité, par masse croissante)
Pour cela je vous propose de reprendre l'algorithme de tri par insertion (ALG03) mais attention ce dernier avait
était établi pour un tri croissant sur un seul critère. Or nous avons ici des tris décroissants et sur 2 critères (le 2ème
critère est le classement en cas d'égalité sur le 1er critère) ….
L'algorithme du tri donné dans ALG03 pour trier dans le sens croissant
VARIABLE
t : tableau d'entiers i : nombre entier j : nombre entier k : nombre entier
DEBUT
j1
tant que j<longueur(t): //boucle 1
ij-1
kt[j]
tant que i≥0 et que t[i]>k: //boucle 2
t[i+1]t[i]
ii-1
fin tant que
t[i+1]k
jj+1
fin tant que
FIN
Adaptons-le pour créer une fonction en Python pour classer un tableau classé par masse décroissante (et en cas
d'égalité des masses, par valeur de l'objet décroissante) :
def classementMasse(tab):
for j in range(1,len(tab)):
i = j-1
k = tab[j] # k désigne un tuple ; k[1] désigne la masse de l'objet et k[2] désigne la valeur de
l'objet
while i>=0 and (tab[i][1] < k[1] or (tab[i][1] == k[1] and tab[i][2] < k[2]))
:
# tab[i][1] < k[1] permet le tri décroissant
# en cas d'égalité tab[i][1] == k[1], on trie par valeur de l'objet décroissante tab[i][2] < k[2]
tab[i+1] = tab[i]
i = i-1
tab[i+1] = k
return tab
Adaptons ce même algorithme pour créer une fonction en Python pour classer un tableau classé par valeur de
l'objet décroissante (et en cas d'égalité des valeurs, par valeur de masse croissante) :
def classementValeur(tab):
for j in range(1,len(tab)):
i = j-1
k = tab[j] # k désigne un tuple ; k[1] désigne la masse de l'objet et k[2] désigne la valeur de
l'objet
while i>=0 and (tab[i][2] < k[2] or (tab[i][2] == k[2] and tab[i][1] > k[1]))
:
# tab[i][2] < k[2] permet le tri décroissant
# en cas d'égalité tab[i][2] == k[2], on trie par masse de l'objet croissante tab[i][1] > k[1]
tab[i+1] = tab[i]
i = i-1
tab[i+1] = k
return tab
Adaptons ce même algorithme pour créer une fonction en Python pour classer un tableau classé par valeur
massique de l'objet décroissante (et en cas d'égalité des valeurs massiques, par valeur de masse croissante) :
def classementValeurMassique(tab): #Il faut d'abord créer une nouvelle donnée dans chaque tuple (la valeur
massique).
newtab=[] # création d'un nouveau tableau
for elt in tab : # boucle pour créer de nouveau tuple contenant une donnée de plus (la valeur massique)
valMas = elt[2]/elt[1] # nouvelle donnée : valeur massique = valeur/masse
elt =(elt[0], elt[1], elt[2], valMas)
# création du nouveau tuple avec 4 données (3 anciennes + valeur massique)
newtab.append(elt) # le nouveau tableau est rempli au fur et à mesure par les nouveaux tuples
for j in range(1,len(newtab)):
i = j-1
k = newtab[j] # k désigne un tuple ; k[3] désigne la valeur massique et k[1] la masse de l'objet
while i>=0 and (newtab[i][3]<k[3] or (newtab[i][3]==k[3] and newtab[i][1]>k[1])) :
newtab[i+1] = newtab[i]
i = i-1
newtab[i+1] = k
return newtab
Autre possibilité pour l'algorithme précédent (donné par Elouan):
def classementValeurMassique2(tab): #sans création d'une nouvelle donnée.
for j in range(1,len(tab)):
i = j-1
k = tab[j] # k désigne un tuple ; k[3] désigne la valeur massique et k[1] la masse de l'objet
while i>=0 and (tab[i][2]/tab[i][1] < k[2]/k[1]
or (tab[i][2]/tab[i][1] == k[2]/k[1] and tab[i][1] > k[1])) :
tab[i+1] = tab[i]
i = i-1
tab[i+1] = k
return tab
On va maintenant mettre en œuvre ces différentes fonctions pour le tableau donné dans l'énoncé :
[("A", 6, 9), ("B", 3, 3), ("C", 5, 8), ("D", 8, 14), ("E", 7, 10), ("F", 4, 7), ("G", 20, 20), ("H", 6, 7), ("I", 2, 3),
("J", 4, 3), ("K", 4, 6), ("L", 10,12), ("M", 2, 2), ("N", 10, 4)]
Pour différentes masses du sac : 19 kg, 25 kg et 45kg
J'ai créé 3 autres fonctions pour avoir les 3 résultats (classement par masse, par valeur et par valeur massique) en
même temps mais ce n'est pas du tout obligatoire :
def resultatTriparMasse(tab,masseSac) :
tabTriee=classementMasse(tab)
return gloutonne (tabTriee, masseSac)
def resultatTriparValeur(tab,masseSac) :
tabTriee=classementValeur(tab)
return gloutonne (tabTriee, masseSac)
def resultatTriparValeurMassique(tab,masseSac) :
tabTriee=classementValeurMassique(tab)
return gloutonne (tabTriee, masseSac)
et pour le script du programme principal qui consiste
à déclarer le tableau et la masse du sac
appeler les fonctions avec comme arguments les 2 variables précédentes
afficher les résultats des 3 classements
data =[("A",6,9),("B",3,3),("C",5,8),("D",8,14),("E",7,10),("F",4,7),("G",20,20),("H",6,7),("I",2,3),
("J",4,3),("K",4,6),("L",10,2),("M",2,2),("N",10,4)]
masseMaxSac = 19
print("Résultat Algorithme Glouton avec masse du sac=",masseMaxSac,"kg")
print("tableau trié par masse décroissante: ",resultatTriparMasse(data,masseMaxSac))
print("tableau trié par valeur décroissante: ",resultatTriparValeur(data,masseMaxSac))
print("tableau trié par valeur massique décroissante:",resultatTriparValeurMassique(data,masseMaxSac))
Les résultats sont :
Résultat Algorithme Glouton avec masse du sac= 19 kg
tableau trié par masse décroissante: (26, ['L', 'D'], 1)
tableau trié par valeur décroissante: (31, ['D', 'D', 'I'], 1)
tableau trié par valeur massique décroissante: (31, ['F', 'F', 'F', 'F', 'I'], 1)
Résultat Algorithme Glouton avec masse du sac= 25 kg
tableau trié par masse décroissante: (28, ['G', 'C'], 0)
tableau trié par valeur décroissante: (28, ['G', 'C'], 0)
tableau trié par valeur massique décroissante: (42, ['F', 'F', 'F', 'F', 'F', 'F'], 1)
Résultat Algorithme Glouton avec masse du sac= 45 kg
tableau trié par masse décroissante: (48, ['G', 'G', 'C'], 0)
tableau trié par valeur décroissante: (48, ['G', 'G', 'C'], 0)
tableau trié par valeur massique décroissante: (77, ['F', 'F', 'F', 'F', 'F', 'F',
'F', 'F', 'F', 'F', 'F'], 1)
À chaque fois le critère de la valeur massique donne le meilleur résultat parmi les 3 proposés (avec une égalité)
mais nous n'avons pas la preuve qu'il s'agisse de la solution la plus optimale possible.