Vous êtes sur la page 1sur 10

2022-2023 TP n° 15 (2 heures) Informatique

Les algorithmes gloutons

I Introduction
Certains algorithmes mis en oeuvre en informatique visent à résoudre des problèmes combinatoires
du type :
• À partir d’une liste de villes que l’on souhaite visiter et d’un tableau des distances entre
ces villes, trouver le circuit le plus court (dit le problème du voyageur de commerce).
• Un commerçant doit régulièrement commander des pièces et billets à la banque pour sa
caisse, ce qui est une contrainte. Il souhaite trouver une stratégie, si possible valable pour
n’importe quel système de monnaie, afin de rendre le minimum de pièces et billets aux
clients (dit le problème du commerçant ou aussi le problème du rendu de monnaie).
• À partir d’une liste de salles et d’une liste de cours (avec une heure de début et une heure de
fin), voire d’autres contraintes, déterminer un planning minimisant l’utilisation des salles.
Ce sont des problèmes d’optimisation pour lesquels on peut raisonnablement vouloir :
1. déterminer s’il existe une solution au problème posé,
2. trouver une solution (même si elle n’est pas unique) qui minimise ou maximise les critères
donnés.
La résolution des problèmes posés ci-dessus n’est pas forcément possible avec une complexité
temporelle polynomiale 1 et on préférera une stratégie permettant de fournir une solution « la
meilleure possible » selon un certain critère. Ainsi,
Les algorithmes gloutons s’appliquent lorsque :
1. la recherche d’une solution peut se ramener à une succession de choix définitifs qui
produisent petit à petit une solution,
2. on dispose d’une méthode de calcul permettant d’évaluer cette solution.
Selon le problème considéré, l’algorithme glouton sera capable (ou non) de fournir une
solution. Cette solution sera ou non optimale pour le problème considéré.

II Le problème du voyageur de commerce


II.1 Une résolution brute
On considère n + 1 ≥ 2 villes numérotées de 0 à n. L’objectif est de partir de la ville 0, de
parcourir chacune des villes et de revenir en 0 en minimisant la distance parcourue. On considère
que l’on dispose d’un tableau V des distances en kilomètres entre les villes. Ainsi, pour tout
(i, j) ∈ J0, nK, V[i][j] est la distance entre la ville i et la ville j.
Afin de tester les fonctions de cette partie, on pourra utiliser le tableau V suivant 2 :
V = [[0, 55, 303, 188, 183], [55, 0, 306, 176, 203], [303, 306, 0, 142, 153],
[188, 176, 142, 0, 123], [183, 203, 153, 123, 0]]
Q1 Écrire une fonction permutations(t:list) -> list qui renvoie une liste de toutes les
permutations des éléments de t. Pour ceux qui sont bloqués, voir le code dans le cours sur
la récursivité.
Ainsi, permutations([1,2,3,4]) renvoie la liste de 4! = 24 tableaux de nombres
[[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], ... , [4, 3, 1, 2], [4, 3, 2, 1]].
1. Pour le problème du voyageur de commerce, on ne connaît pas de solution générale en une complexité
polynomiale.
2. Il s’agit dans l’ordre des distances respectives entre Nancy, Metz, Paris, Reims et Troyes.

Lycée Michelet - CPGE 1re année 1


2022-2023 TP n° 15 (2 heures) Informatique

Q2 Écrire une fonction longueur(itineraire:list, V:list) -> int, où itineraire est


une permutation de [1, ..., n], avec n =len(V)−1, et qui calcule la longueur du circuit
formé des villes suivantes : 0 - itineraire[0] - itineraire[1] - ... - itineraire[n−1] - 0.
Par exemple, longueur([1, 2, 3, 4], V) renvoie 809 = 55 + 306 + 142 + 123 + 183.
Q3 Écrire une fonction longueur_minimale(V:list) -> int qui, à partir de la liste de tous
les itinéraires possibles de n villes (numérotées de 1 à n) renvoie la longueur minimale des
itinéraires possibles (la longueur étant définie telle qu’à la question précédente, donc de la
ville 0 vers et par les villes de l’itinéraire, avec un retour à la ville 0).
La fonction longueur_minimale(V) doit renvoyer 709.
Q4 Montrer que la complexité de la fonction longueur_minimale est au moins en O(n!).
Il y a plus de 3 millions d’itinéraires pour 10 villes, plus d’un milliard de milliard d’itinéraires
pour 20 villes,...

II.2 Une résolution approchée par un algorithme glouton


Le principe général de l’algorithme glouton est le suivant.
Les villes sont numérotées de 0 à n, avec n =len(V)−1. On maintient un dictionnaire des villes
visitées, afin de ne pas les parcourir deux fois : par exemple, {0 : T rue, 2 : T rue} quand les villes
0 et 2 ont été visitées, les autres villes n’étant pas dans le dictionnaire. À chaque ville (en partant
de la ville 0), on choisit systématiquement la ville non parcourue la plus proche. On itère jusqu’à
ce que toutes les villes aient été parcourues.
Q5 Écrire une fonction plus_proche(ville:int, V:list, visitees:dict = {0:True}) ->
int qui sélectionne la ville non déjà parcourue, la plus proche de ville.
Par exemple, plus_proche(2, V) renvoie 3 et plus_proche(2, V, {0:True, 3:True})
renvoie 4.
Q6 Écrire une fonction voyageur(V:list) -> (list, int) qui renvoie l’itinéraire choisi (sous
forme de liste) et la longueur du circuit.
Ainsi, voyageur(V) renvoie ([1, 3, 4, 2], 810).
Q7 Quelle est la complexité de cet algorithme ?
On constate que le circuit est plus long que le circuit minimal de 709 kilomètres, mais il est de
loin meilleur que beaucoup d’autres circuits ! De plus, il est déterminé en un temps beaucoup
plus court.
Autre exemple de mise en échec de l’optimalité du chemin de l’algorithme glouton (avec 4 villes) :

Figure 1 – Chemin le plus court : A-B-C-D-A ; chemin glouton : A-C-B-D-A

Remarque : il a été démontré que, pour un grand nombre de villes, le rapport entre la distance
de la solution gloutonne et celle de la solution optimale est dans le pire des cas proportionnel au
logarithme du nombre de villes.

Lycée Michelet - CPGE 1re année 2


2022-2023 TP n° 15 (2 heures) Informatique

III Le problème du rendu de monnaie


Considérons un système de monnaie avec des pièces ou billets de 1€, 2€, 5€, 10€, 20€, 50€, 100€
ou 200€ (pour simplifier, nous ne considérerons pas les centimes). Comment faire pour rendre un
minimum de pièces/billets à un client ? S’il faut rendre de petits montants, tel que 9€, c’est aisé
de tête 3 , mais pour des montants plus élevés ?
Q8 Soit euros = (1, 2, 5, 10, 20, 50, 100, 200) le système de monnaie de référence 4 .
On propose d’utiliser un algorithme glouton qui rend la plus grosse coupure possible, puis
recommence sur le montant restant.
Écrire une fonction rendu(monnaie:int, systeme:tuple) -> (list, int) qui met en
oeuvre cet algorithme glouton et renvoie la liste des pièces/billets à rendre ainsi que le
nombre de pièces/billets. On fournit à la fonction en entrée le montant à rendre (en €)
ainsi que le tuple du système de monnaie de référence.
Par exemple, rendu(9, euros) renvoie ([5, 2, 2], 3) et rendu(199, euros) renvoie
([100, 50, 20, 20, 5, 2, 2], 7).
Q9 On veut faire comprendre pourquoi l’algorithme glouton proposé devrait fournir une solution
optimale au problème posé :
— Pourquoi l’algorithme se termine-t-il ?
— Dans un rendu optimal pour un montant inférieur à 199€, combien faut-il rendre au
plus de pièces de 1€, de 2€, et ainsi de suite ?
— En déduire qu’il faut forcément rendre un billet de 200€ dès que le montant à rendre
dépasse 200€. Procéder de même pour les montants inférieurs.
— Conclure.
Q10 On donne un nom particulier à un système de monnaie pour lequel l’algorithme glouton
donne un rendu optimal : on dit qu’il est « canonique ».
— Que renvoie rendu(12, [1,6,10]) ? Qu’en pensez-vous ?
— Même question avec rendu(4, [2,3]).

IV Le problème des emplois du temps 5


L’objectif est de répartir des cours dans des salles, en essayant d’utiliser le moins de salles
possibles grâce à un algorithme glouton.
Q11 Pour simplifier le problème, on va considérer des cours de 1 heure ou de 2 heures.
Chaque cours est un triplet [n,d,f] où n est le numéro du cours, d son heure de début et
f son heure de fin.
Les cours du matin s’échelonnent de 8 heures à 12 heures et ceux de l’après-midi s’échelonnent
de 13 heures à 18 heures. Aucun cours ne peut se terminer le matin au-delà de 12 heures et
l’après-midi au-delà de 18 heures. De plus, 70% des cours ont une durée de 1 heure.
Écrire une fonction generer_cours(N:int)->list qui renvoie une liste de cours en tenant
compte des contraintes précédentes.
Par exemple, generer_cours(5) peut renvoyer [[0, 17, 18], [1, 8, 10], [2, 8, 9],
[3, 17, 18], [4, 8, 9]].
On importera du module random la fonction random qui génère un nombre aléatoire dans
3. il y a 8 possibilités. Le nombres de pièces/billet optimal est : 2 pièces de 2€ et 1 billet de 5€.
4. On utilise un tuple car les données ne sont pas modifiables.
5. d’après ITC - nouveaux programmes de Thierry Audibert et Amar Oussalah

Lycée Michelet - CPGE 1re année 3


2022-2023 TP n° 15 (2 heures) Informatique

[0,1[ et la fonction choice telle que choice(liste) sélectionne au hasard un élément


d’une liste.
Q12 Écrire une fonction compatibles(c1:list, c2:list) -> bool qui reçoit deux listes de
la forme [d,f], où d est l’heure de début et f est l’heure de fin, et renvoie True si les
horaires ne se chevauchent pas et False sinon.
Par exemple, compatibles([8,9],[9,10]) renvoie True et compatibles([9,11],[8,10])
renvoie False.
Q13 Soit planning un dictionnaire de la forme {s:[[n1,d1,f1],...[nk,dk,fk]],...]}, où
s est l’une des salles (un entier naturel) et les [ni,di,fi] les cours dans la salle s.
Par exemple, planning peut valoir {0:[[0,8,9],[1,9,11]], 1:[[3,8,9]], 2:[]}.
Écrire une fonction salles_incompatibles(planning:dict, c0:list) -> dict qui ren-
voie les salles indisponibles à l’horaire du cours c0.
Par exemple, salles_incompatibles(planning,[13,8,10]), avec planning de l’exemple
ci-dessus, renvoie {0:True, 1:True}.
Nous disposons à présent de toutes les fonctions nous permettant d’écrire l’algorithme glouton
qui va répartir les cours dans les salles.
— Nous admettrons que l’algorithme fournit une solution optimale si les cours sont rangés
dans l’ordre d’heure de fin croissante. Comme, il s’agit d’une liste de listes, nous
utiliserons la fonction sort de Python pour les trier.
1 N = 35 # nombre de cours
2 cours = generer_cours ( N ) # liste des cours
3 cours . sort ( key = lambda c : c [2]) # tri selon l ' heure de fin

— L’algorithme utilise ensuite une fonction qui parcourt la liste des cours et recherche la
première salle disponible à chaque itération. Si l’une des salles est disponible, il l’inscrit
dans le planning, sinon l’algorithme échoue (les salles sont donc toutes occupées) et il
renvoie cette information d’échec. L’algorithme produit ainsi un planning qui optimise
l’utilisation des salles.
Q14 Écrire allocation_salles(cours:list, nbre_salles:int) -> (bool, dict, int) qui,
à partir de la liste de cours, du nombre de salles disponibles (notées de 0 à nbre_salles − 1)
renvoie un tuple avec un booléen indiquant si l’algorithme a réussi ou échoué, le planning
des cours et le numéro de la dernière salle attribuée (ce qui est utile en cas d’échec).
Par exemple, print(allocation_salles(cours, 10)) renvoie (True, {0: [[4, 9, 10],
[1, 10, 11], [3, 11, 12], [6, 13, 14], [18, 14, 15], [26, 15, 16], [11, 16,
17], [0, 17, 18]], 1: [[8, 9, 10], [17, 10, 11], [9, 11, 12], [21, 13, 14],
[20, 14, 16], [13, 16, 17], [33, 17, 18]], 2: [[10, 9, 10], [22, 10, 11],
[19, 11, 12], [2, 13, 15], [29, 15, 16], [24, 16, 17], [34, 17, 18]],
3: [[27, 8, 10], [28, 10, 11], [30, 11, 12], [5, 13, 15], [12, 15, 17]],
4: [[7, 10, 12], [15, 15, 17]], 5: [[14, 10, 12], [25, 16, 17]],
6: [[16, 10, 12], [31, 16, 18]], 7: [[23, 10, 12]], 8: [[32, 10, 12]],
9: []}, 2).

Lycée Michelet - CPGE 1re année 4


2022-2023 TP n° 15 (2 heures) Informatique

Corrigé
Q1
5 def permutations ( t : list ) -> list :
6 ''' renvoie une liste contenant les permutations de t '''
7 # Pr é conditions : t est un tableau à 1 dimension
8 # Postconditions : une liste l de listes ; chacune d ' elle contient les
é l é ments de t qui ont é t é permut é s
9 if len ( t ) <= 1:
10 return [ t ]
11 else :
12 p = permutations ( t [1:])
13 l = []
14 for permut in p :
15 for i in range ( len ( permut ) +1) :
16 l . append ( permut [: i ]+[ t [0]]+ permut [ i :])
17 return l

Q2
20 def longueur ( itineraire : list , V : list ) -> int :
21 ''' renvoie la longueur d ' un circuit de la ville 0 à elle - m ê me , en
passant par les villes de la variable itineraire '''
22 # pr é conditions : la variable itineraire est suppos é e non vide
23 # postconditions : un entier positif
24 n = len ( itineraire )
25 v = itineraire [0]
26 d = V [0][ v ] # ville 0 à premi è re ville
27
28 for i in range (1 , n ) : # ville i à ville i +1
29 w = itineraire [ i ]
30 d = d + V [ v ][ w ]
31 v = w
32

33 d = d + V [ v ][0] # derni è re ville à ville 0


34
35 return d
36
37 t = permutations ([1 ,2 ,3 ,4])
38 assert longueur ( t [0] , V ) == 809

Q3
41 from math import inf
42
43 def l ongueu r_min imale ( V : list ) -> int :
44 ''' renvoie la plus petite longueur parmi toutes les longueurs de
circuits '''
45 # pr é conditions : n est un entier naturel non nul
46 # postconditions : un entier positif
47 n = len ( V ) - 1
48 t = permutations ( list ( range (1 , n +1) ) )
49 l = inf
50 for itineraire in t :
51 if longueur ( itineraire , V ) < l :
52 l = longueur ( itineraire , V )
53 return l
54
55 assert lo ngueur _minim ale ( V ) == 709

Q4 La fonction permutations a une complexité en O(n!), donc la fonction longueur_minimale


qui utilise cette fonction a une complexité au moins en O(n!).

Lycée Michelet - CPGE 1re année 5


2022-2023 TP n° 15 (2 heures) Informatique

Q5
60 def plus_proche ( ville : int , V : list , visitees : dict = {0: True }) -> int :
61 ''' renvoie le num é ro de la ville non d é j à parcourue la plus proche
de celle de la variable ville '''
62 # pr é conditions : la variable ville est un num é ro compris entre 1 et
n ; la variable visitees est un ensemble de num é ros de villes
63 # postconditions : un entier positif compris entre 1 et n
64 d = inf # distance minimale
65 pp = ville # ville la plus proche
66 dist_ville = V [ ville ]
67 for v in range ( len ( dist_ville ) ) :
68 if v not in visitees . keys () : # on ne traite que les villes non
visit é es
69 if v != ville and dist_ville [ v ] < d :
70 d = dist_ville [ v ]
71 pp = v
72 return pp
73
74 assert plus_proche (2 , V ) == 3
75 assert plus_proche (2 , V , {0: True , 3: True }) == 4

Q6
78 def voyageur ( V : list ) -> ( list , int ) :
79 ''' renvoie un itin é raire localement optimal et sa longueur via un
algorithme glouton '''
80 # pr é conditions : la variable n est un entier naturel non nul
81 # postconditions : un couple form é d ' une liste de num é ros de villes
et un entier positif d é signant une longueur
82 n = len ( V ) - 1
83 v = 0
84 visitees = {0: True }
85 itineraire = []
86 d = 0
87
88 for i in range ( n ) :
89 w = plus_proche (v , V , visitees ) # v est la ville la plus proche
90 visitees [ w ] = True # on la d é clare visit é e
91 itineraire . append ( w ) # on l ' ajoute à l ' itin é raire
92 d = d + V [ v ][ w ]
93 v = w
94
95 d = d + V [ v ][0]
96 return itineraire , d
97
98 print ( voyageur ( V ) )

Q7 Soit n le nombre de villes. La fonction plus_proche a une complexité en O(n).


En effet, V[ville] a toujours une longueur de n, donc la boucle for parcourt n éléments.
Ensuite, la recherche dans visitees se fait en O(1) car on a utilisé un accès à un dictionnaire.
D’où la complexité donnée.
La complexité de la fonction voyageur est donnée par la boucle for qui appelle n fois
plus_proche. Toutes les autres instructions sont en O(1). Par conséquent, la fonction
voyageur a une complexité en O(n2 ).

Lycée Michelet - CPGE 1re année 6


2022-2023 TP n° 15 (2 heures) Informatique

Q8
104 def rendu ( monnaie : int , systeme : tuple ) -> ( list , int ) :
105 montant = monnaie
106 ''' renvoie une liste minimale de pi è ces / billets à rendre gr â ce à un
algorithme glouton '''
107 # pr é conditions : la variable monnaie est un entier naturel ( montant
en euros sans centimes ) qui correspond au montant à rendre et la
variable syst è me est un tuple de pi è ces / billets autoris é s
108 # postconditions : un couple form é d ' une liste de pi è ces / billets ( des
entiers positifs ) et un entier positif d é signant la longueur du
tuple
109 i = len ( systeme ) -1
110 t = []
111 while montant > 0:
112 if montant >= systeme [ i ]: # on choisit de fa ç on r é p é t é e la pi è ce
/ le billet le plus é lev é du syst è me de monnaie de r é f é rence
113 montant = montant - systeme [ i ]
114 t . append ( systeme [ i ])
115 else :
116 i = i -1 # quand ce n ' est plus possible , on change de pi è ce /
le billet du syst è me de monnaie de r é f é rence
117 return t , len ( t )
118

119 euros = (1 , 2 , 5 , 10 , 20 , 50 , 100 , 200) # tuple de r é f é rence


120 assert rendu (9 , euros ) == ([5 , 2 , 2] , 3)
121 assert rendu (199 , euros ) == ([100 , 50 , 20 , 20 , 5 , 2 , 2] , 7)

Q9 On veut faire comprendre pourquoi l’algorithme glouton proposé devrait fournir une solution
optimale au problème posé :
— Pourquoi l’algorithme se termine-t-il ?
La variable montant est un variant de la boucle while. En effet, si le montant
est supérieur à un valeur de référence du système euros, il décroît strictement via
montant = montant - systeme[i]. Dans le pire des cas, on utilise 1 pièce de 1€
quand le montant est strictement inférieur à 2€, ce qui réduit le montant à 0€. Ainsi,
l’algorithme se finit toujours.
— Dans un rendu optimal pour un montant inférieur à 199€, combien faut-il rendre au
plus de pièces de 1€, de 2€, et ainsi de suite ?
Dans un rendu optimal, pour un montant inférieur à 199€, dès que l’on doit donner 2
pièces de 1€, il est préférable de rendre 1 pièce de 2€ : on doit donc rendre au plus 1
pièce de 1€. Avec le même raisonnement,
• les pièces/billets de 1€, 5€, 10€, et 100€ ne sont donnés qu’au plus 1 fois,
• les pièces/billets de 2€ et 20€ ne sont donnés qu’au plus 2 fois,
• et on ne peut donner à la fois 2 pièces de 2€ et 1 pièce de 1€ (on rendra alors
plutôt 1 billet de 5€) ni donner à la fois 2 billets de 20€ et 1 billet de 10€.
— En déduire qu’il faut forcément rendre un billet de 200€ dès que le montant à rendre
dépasse 200€. Procéder de même pour les montants inférieurs.
• La somme totale des pièces/billets d’un rendu optimal ne peut donc dépasser
1 × 100 + 1 × 50 + 2 × 20 + 1 × 5 + 2 × 2 = 199. Dans un rendu optimal, toute
somme au-dessus de 200€ contiendra donc au moins 1 billet de 200€. Ainsi, si le
montant à rendre est supérieur à 200€, il faut commencer par rendre un billet de
200€.
• De même, la somme totale des pièces/billets d’un rendu optimal avec des pièces et
billets de 1€, 2€, 5€, 10€ et 20€ et 50€ est alors 1 × 50 + 2 × 20 + 1 × 5 + 2 × 2 = 99.

Lycée Michelet - CPGE 1re année 7


2022-2023 TP n° 15 (2 heures) Informatique

Dans un rendu optimal, toute somme au-dessus de 100€ et inférieur à 199€


contiendra donc au moins 1 billet de 100€.
• et ainsi de suite.
— Conclure.
Nous venons de décrire les caractéristiques d’une solution optimale.
Or l’algorithme glouton recherche effectivement le nombre de billets de 200€ tant que
le montant est supérieur à 200€, puis de 100€ tant que le montant restant est entre
100€ et 199€ et ainsi de suite. Ainsi, comme l’algorithme glouton se termine, il fournit
une solution optimale.
Q10
126 >>> rendu (12 ,[1 ,6 ,10])
127 ([10 , 1 , 1] , 3)
128 # Le montant n ' est pas optimal car 2 * 6 euros est meilleur .
129
130 >>> rendu (4 ,[2 ,3])
131 if montant >= systeme [ i ]:
132 IndexError : list index out of range
133 Une fois 3 euros rendu , il reste 1 euro à rendre , ce que ne permet pas
le syst è me mon é taire .

Les systèmes monétaires proposés ne sont donc pas canoniques.


Q11
136 import random as rd
137
138 def generer_cours ( N : int ) -> list :
139 ''' renvoie une liste de N cours dont 70% de dur é e à 1 heure et 30% à
2 heures . Les cours se d é roulent de 8:00 à 12:00 , puis de 13:00 à
18:00 et ne finissent jamais apr è s 12:00 ou 18:00 '''
140 # pr é conditions : N est un entier naturel
141 # postconditions : une liste de listes ; chacune d ' elle est compos é e
de trois entiers ( le num é ro du cours , l ' heure de d é but du cours et l '
heure de fin de cours )
142 cours = []
143 for n in range ( N ) :
144 u = rd . random ()
145 if u < 0.7:
146 # cours de 1 heure
147 horaire1 = (8 ,9 ,10 ,11 ,13 ,14 ,15 ,16 ,17)
148 d = rd . choice ( horaire1 )
149 cours . append ([ n ,d , d +1])
150 # cours de 2 heures
151 else :
152 horaire2 = (8 ,9 ,10 ,13 ,14 ,15 ,16)
153 d = rd . choice ( horaire2 )
154 cours . append ([ n ,d , d +2])
155 return cours

Lycée Michelet - CPGE 1re année 8


2022-2023 TP n° 15 (2 heures) Informatique

Q12
158 def compatibles ( c1 : list , c2 : list ) -> bool :
159 ''' renvoie True si les intervalles de temps des cours c1 et c2 ne
se chevauchent pas ( ou au plus en 1 valeur ) et False dans le cas
contraire '''
160 # pr é conditions : c1 et c2 s 'é crivent [d , f ] avec d l ' heure de d é but ,
et f l ' heure de fin
161 # postconditions : True ou False
162 d1 , f1 = c1
163 d2 , f2 = c2
164 return d1 >= f2 or d2 >= f1
165
166 # Tests unitaires
167 assert compatibles ([8 ,9] , [9 ,10]) == True
168 assert compatibles ([9 ,11] , [8 ,9]) == True
169 assert compatibles ([8 ,10] , [9 ,10]) == False
170 assert compatibles ([9 ,11] , [8 ,10]) == False
171 assert compatibles ([13 ,14] , [9 ,10]) == True

Q13
174 def s a ll e s _ i n c o m p a t i b l e s ( planning : dict , c0 : list ) -> dict :
175 ''' renvoie l ' ensemble des salles du planning incompatibles avec les
horaires du cours c0 '''
176 # pr é conditions : planning = { s :[[ n1 , d1 , f1 ] ,[ n2 , d2 , f2 ] ,...[ nk , dk , fk
]] ,...} avec s une salle et [ di , fi ] des cr é neaux horaires ; c0 est de
la forme [n ,d , f ] , trois entiers
177 # postconditions : un ensemble de type dict avec des numeros de
salles ( des entiers positifs ) et le bool é en True
178 n0 , d0 , f0 = c0
179 salles_occupees = {}
180 for salle , occupation in planning . items () :
181 for c in occupation :
182 n ,d , f = c
183 if not compatibles ([ d , f ] ,[ d0 , f0 ]) :
184 salles_occupees [ salle ] = True
185 break # on interrompt la boucle for int é rieure
186 return salles_occupees
187

188 # Tests unitaires


189 planning = {0:[[0 ,8 ,9] ,[1 ,9 ,11] ,[2 ,13 ,14]] ,
1:[[3 ,8 ,9] ,[4 ,15 ,16] ,[5 ,16 ,18]] , 2:[[6 ,10 ,12] ,[7 ,15 ,16] ,[8 ,16 ,18]]}
190 assert s a l l e s _ i n c o m p a t i b l e s ( planning ,[9 ,9 ,11]) == {0: True , 2: True }
191 assert s a l l e s _ i n c o m p a t i b l e s ( planning ,[10 ,15 ,18]) == {1: True , 2: True }
192 assert s a l l e s _ i n c o m p a t i b l e s ( planning ,[11 ,8 ,9]) == {0: True , 1: True }
193
194 planning = {0:[] , 1:[] , 2:[]}
195 assert s a l l e s _ i n c o m p a t i b l e s ( planning ,[12 ,9 ,11]) == {}
196
197 planning = {0:[[0 ,8 ,9] ,[1 ,9 ,11]] , 1:[[3 ,8 ,9]] , 2:[]}
198 assert s a l l e s _ i n c o m p a t i b l e s ( planning ,[13 ,8 ,10]) == {0: True , 1: True }

Lycée Michelet - CPGE 1re année 9


2022-2023 TP n° 15 (2 heures) Informatique

Q14
201 def a llocat ion_s alles ( cours : list , nbre_salles : int ) -> ( bool , dict , int ) :
202 # renvoie un planning d ' allocation des salles pour une liste de
cours gr â ce à un algorithme glouton ; la fonction indique un é ventuel
cas d 'é chec ( trop de cours par rapport aux salles ) ou le succ è s dans
l ' attribution des salles ''
203 # pr é conditions : la variable cours est une liste de cours ( de la
forme [n ,d , f ]) , la variable nbre_salles est un entier naturel
204 # postconditions : un tuple avec un bool é en de succ è s ou d 'é chec de l
' algorithme , un planning de la forme { s :[[ n1 , d1 , f1 ] ,[ n2 , d2 , f2 ] ,...[ nk
, dk , fk ]] ,...} avec s une salle et [ di , fi ] des cr é neaux horaires , et
un entier correspondant au dernier num é ro de salle attribu é ( un
entier naturel , sauf en cas de nbre_salles é gal à 0 o ù la fonction
renvoie -1)
205 planning = dict ()
206 for i in range ( nbre_salles ) :
207 planning [ i ] = []
208
209 for C in cours :
210 # print (C , planning )
211 salles_pb = s a l l e s _ i n c o m p a t i b l e s ( planning , C )
212 # print ( salles_pb )
213 i = 0
214 while i < nbre_salles and i in salles_pb : # recherche premi è re
salle disponible
215 i = i + 1
216
217 if i != nbre_salles : # si une salle est inoccup ée , on l ' affecte
218 planning [ i ]. append ( C )
219 else :
220 return False , planning , planning , i -1 # sinon on indique un
probl è me d ' affectation
221 return True , planning , i # toutes les salles ont é t é affect é es
222

223 # Tests unitaires


224 N = 35 # nombre de cours
225 cours = generer_cours ( N ) # liste des cours
226 cours . sort ( key = lambda c : c [2]) # tri selon l ' heure de fin
227 print ( alloc ation_ salles ( cours ,10) )

Lycée Michelet - CPGE 1re année 10

Vous aimerez peut-être aussi