Vous êtes sur la page 1sur 23

Numéro d’anonymat (à remplir par un surveillant)

Examen 2016 – 2017


Éléments de Programmation I – 1I001
Durée : 2h
Documents autorisés: Aucun document ni machine électronique n’est autorisé à l’exception de
la carte de référence de Python.
Le sujet comporte 23 pages. Ne pas désagrafer les feuilles.
Répondre directement sur le sujet, dans les boîtes appropriées. La taille des boîtes suggère le
nombre de lignes de la réponse attendue. Le quadrillage permet d’aligner les indentations.
Le barème indiqué pour chaque question n’est donné qu’à titre indicatif. Le barème total est lui
aussi donné à titre indicatif : 66 points auxquels peuvent s’ajouter des points bonus explicités dans
l’énoncé des questions.
La clarté des réponses et la présentation des programmes seront appréciées. Les exercices peuvent
être résolus dans un ordre quelconque. Pour répondre à une question, il possible et souvent
utile, d’utiliser les fonctions qui sont l’objet des questions précédentes, même si vous
n’avez répondu à ces questions précédentes.
Remarque: si nécessaire, on considère que la bibliothèque de fonctions mathématiques a été
importée avant les fonctions à écrire. Sauf mention contraire explicite, seules les primitives Python
présentes sur la carte de référence peuvent être utilisées.
Important: bien lire ce qui est demandé dans l’intitulé de la question. Sauf exception (ex.: fonction
mystère), pour les fonctions demandées, il est nécessaire de donner une définition avec
signature et hypothèse(s) éventuelle(s), mais pas nécessaire de donner la description ou un
jeu de test.
Notation des spécifications: Une spécification (signature et hypothèses) seule ne rapporte pas
de point. Une fonction correcte dont la spécification est fausse ou manquante est pénalisée.
L’examen est composé de quatre exercices indépendants :
— Exercice 1 : Organisation du festival Elem-o-Prog – (p. 2)
— Exercice 2 : Réseaux Sociaux – (p. 6)
— Exercice 3 : Ensemble de Mandelbrot – (p. 11)
— Exercice 4 : Analyse de Code – (p. 15)

UPMC
c - Informatique - 1I001 - page 1 -
Exercice 1 : Organisation du festival Elem-o-Prog
Chaque année, le Festival Elem-o-Prog a lieu le 1er janvier, entre 0h et 24h.
Les groupes de musique envoient une demande de concert, sous la forme d’une chaîne de caractères
avec un format bien précis : ’XX-nom-YY’. Les 2 caractères XX correspondent à l’heure de début
(un nombre entier à 2 chiffres compris entre 00 et 23) et les caractères YY correspondent à l’heure
de fin (un nombre entier à 2 chiffres compris entre 01 et 24) tels que XX < YY. Enfin ’nom’ est
le nom de leur groupe.
Ainsi la chaîne ’06-LesPtitsDej-10’ correspond au groupe ’LesPtitsDej’ qui souhaite
jouer de 6h à 10h.
L’exercice consiste à sélectionner des concerts dont les horaires sont compatibles.

Question 1.1 : [2/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction extraction_concert
qui, étant donné une demande (sous la forme ’XX-nom-YY’), renvoie le triplet concert du style
sous la forme (’nom’, XX, YY) dont les éléments sont respectivement de type str, int et
int. Par exemple :
>>> extraction_concert(’02-LeveTot-05’)
(’LeveTot’, 2, 5)
>>> extraction_concert(’06-LesPtitsDej-10’)
(’LesPtitsDej’, 6, 10)

Rappel : la fonction int(s) transforme la chaîne de caractères représentant un entier en un


entier.
>>> int( ’12’ )
12
>>> int( ’05’ )
5

Réponse

def extraction_concert(demande):
""" str -> (str, int, int)
H: demande est bien formattee"""

return (demande[3:-3], int(demande[:2]), int(demande[-2:]))

Les demandes d’inscription sont enregistrées dans la liste de demandes suivante :


# Demandes : list[str]
Demandes = [ ’07-Les7:9-09’, ’22-LesNoctambules-23’, ’06-LesPtitsDej-10’,
’06-Aube-07’, ’02-LeveTot-05’]

Question 1.2 : [3/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction liste_concerts
qui, étant donné une liste de demandes (contenant des éléments sous la forme ’XX-nom-YY’),
renvoie la liste des triplets de concerts du style (’nom’, XX, YY). Ainsi,

UPMC
c - Informatique - 1I001 - page 2 -
>>> liste_concerts(Demandes)
[(’Les7:9’, 7, 9), (’LesNoctambules’, 22, 23), (’LesPtitsDej’, 6, 10),
(’Aube’, 6, 7), (’LeveTot’, 2, 5)]

Réponse

def liste_concerts(L):
""" list[str] -> list[ (str, int, int) ] """

# R : list[(str, int, int)]


R = []
# d : str
for d in L:
R.append(extraction_concert(d))

return R

La liste des triplets de concerts est triée suivant l’heure de début de chaque concert. Voilà le
résultat obtenu :
# Concerts : list[(str, int, int)]
Concerts = [ (’LeveTot’,2, 5), (’LesPtitsDej’, 6, 10), (’Aube’,6, 7),
(’Les7:9’, 7, 9), (’LesNoctambules’, 22, 23) ]

On reçoit une nouvelle demande d (de type str). On veut insérer le triplet concert associé à d,
dans la liste de triplets déjà triée suivant l’heure de début (la liste est éventuellement vide). On
veut le placer à la première position possible qui respecte l’ordre croissant de début de
concert.
Question 1.3 : [4/66]
Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction indice qui,
étant donné une demande d, et une liste de triplets concerts triée Concerts, renvoie l’indice
où l’insertion doit se faire. Par exemple :
>>> indice(’20-LeVingtHeure-21’, Concerts)
4
>>> indice(’22-LesDer-24’, Concerts)
4
>>> indice(’23-GongFinal-24’, Concerts)
5
>>> indice(’20-LeVingtHeure-21’, [])
0

Attention : la fonction indice peut ne pas être correcte si en entrée, la liste n’est pas triée
suivant le début de concert.

UPMC
c - Informatique - 1I001 - page 3 -
Réponse

def indice(d, L):


""" str * list[(str, int, int)]-> int
Hypothese : L est triee suivant le dàbut des concerts """

# nom:str, deb:int fin:int


nom, deb, fin = extraction_concert(d)

# i : int indice courant


i = 0
while i<len(L):
# nomL:str, debL:int, finL:int
nomL, debL, finL = L[i]
if deb > debL:
i = i+1
else:
return i
return i

Question 1.4 : [2/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction insertion qui,
étant donnée la nouvelle demande d, et une liste de triplets concerts triée L, renvoie une nouvelle
liste dans laquelle le concert associé à d a été inséré. Par exemple :
>>> insertion(’20-LeVingtHeure-21’, Concerts)
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’Aube’, 6, 7), (’Les7:9’, 7, 9),
(’LeVingtHeure’, 20, 21), (’LesNoctambules’, 22, 23)]
>>> insertion(’22-LesDer-24’, Concerts)
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’Aube’, 6, 7), (’Les7:9’, 7, 9),
(’LesDer’, 22, 24), (’LesNoctambules’, 22, 23)]
>>> insertion(’23-GongFinal-24’, Concerts)
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’Aube’, 6, 7), (’Les7:9’, 7, 9),
(’LesNoctambules’, 22, 23), (’GongFinal’, 23, 24)]
>>> insertion(’20-LeVingtHeure-21’, [])
[(’LeVingtHeure’, 20, 21)]

Réponse

def insertion(d, L):


""" str * list[(str, int, int)]-> list[(str, int, int)]
Hypothese : L est triee suivant le debut des concerts """

#i : int
i = indice(d, L)

return L[:i] + [extraction_concert(d)] + L[i:]

On définit Concerts2, le résultat des deux insertions suivantes :


# Concerts2 : list[ (str, int, int) ]
>>> Concerts2 = insertion(’20-LeVingtHeure-21’, insertion(’22-LesDer-24’, Concerts))

UPMC
c - Informatique - 1I001 - page 4 -
>>> Concerts2
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’Aube’, 6, 7), (’Les7:9’, 7, 9),
(’LeVingtHeure’, 20, 21), (’LesDer’, 22, 24), (’LesNoctambules’, 22, 23)]

Question 1.5 : [1/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction est_compatible
qui, étant donné deux triplets concerts, concert1 et concert2, renvoie True si la fin de
concert1 est avant le début de concert2. Sinon, la fonction renvoie False.
Ainsi, on obtient :
>>> est_compatible( (’Aube’, 6, 7), (’LesNoctambules’, 22, 23))
True
>>> est_compatible( (’Aube’, 6, 7), (’Les7:9’, 7, 9))
True
>>> est_compatible( (’LesPtitsDej’, 6, 10), (’Les7:9’, 7, 9))
False
>>> est_compatible((’LesNoctambules’, 22, 23), (’LeveTot’, 2, 5))
False

Réponse

def est_compatible(concert1, concert2):


""" list[(str, int, int)]**2 -> bool """
# n1, n2 : str
# d1, f1, d2, f2 : int
n1, d1, f1 = concert1
n2, d2, f2 = concert2

return f1 <= d2

Question 1.6 : [4/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction liste_compatible
qui, étant donné une liste de triplets concerts triée L (suivant le début du concert), renvoie une
nouvelle liste ne conservant que les triplets compatibles.
>>> liste_compatible(Concerts)
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’LesNoctambules’, 22, 23)]
>>> liste_compatible(Concerts2)
[(’LeveTot’, 2, 5), (’LesPtitsDej’, 6, 10), (’LeVingtHeure’, 20, 21),
(’LesDer’, 22, 24)]

UPMC
c - Informatique - 1I001 - page 5 -
Réponse

def liste_compatible(L):
""" list[(str, int, int)] -> list[(str, int, int)] """

if L == []:
return []

# R : list[s(str, int, int)] liste resultat


R = [ L[0] ]

# i : int
for i in range(1, len(L)):
if est_compatible(R[-1], L[i]):
R.append(L[i])
return R

Exercice 2 : Réseaux Sociaux


Dans cet exercice, on s’intéresse à une modélisation de deux types de réseaux sociaux sous forme
de dictionnaires.
Dans un premier temps, on modèlise un réseau social comme un dictionnaire (de type dict[str:set[str]])
dont les clefs sont les personnes inscrites dans le réseau et où la valeur associée à une personne p
est l’ensemble des personnes du réseau que p a comme ami.
Attention : la relation d’amitié représentée par un réseau n’est pas forcément réciproque. Par
exemple ’Amine’ peut avoir ’Daniel’ dans ses amis sans que ’Daniel’ ait ’Amine’ dans
ses amis.
On utilise l’alias de type Reseau :
# type Reseau = dict[str:set[str]]

On suppose que dans un Reseau, les valeurs ne sont composées que d’éléments qui sont des clefs
du réseau. On ne peut pas avoir comme amie une personne qui n’est pas dans le réseau.
Pour les tests, on considère le réseau suivant :
VL = {’Amine’ : {’Barbara’, ’Daniel’, ’Frank’},
’Barbara’: {’Amine’, ’Charles’, ’Daniel’},
’Charles’: {’Barbara’},
’Daniel’: {’Charles’, ’Barbara’},
’Emilie’: set(),
’Frank’: {’Amine’, ’Daniel’}}

Question 2.1 : [1/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction nb_amis qui
prend en entrée un réseau res, une personne p de res et renvoie le nombre de personnes que p
a comme ami dans res.
>>> nb_amis(VL, ’Amine’)
3
>>> nb_amis(VL, ’Emilie’)
0

UPMC
c - Informatique - 1I001 - page 6 -
Réponse

def nb_amis(reseau, pers):


""" Reseau * str -> int
H: pers in reseau"""

return len(reseau[pers])

Question 2.2 : [3/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction amis_avec qui
prend en entrée un réseau res, une personne p de res et renvoie l’ensemble des personnes de
res qui ont p comme amie.
>>> amis_avec(VL, ’Amine’)
{’Barbara’, ’Frank’}
>>> amis_avec(VL, ’Charles’)
{’Barbara’, ’Daniel’}
>>> amis_avec(VL, ’Emilie’)
set()

Réponse

def amis_avec(reseau, pers):


""" Reseau * str -> set[str]
H: pers in reseau"""

return {p for p in reseau if pers in reseau[p]}

Question 2.3 : [3/66]


On définit le score d’une personne comme le nombre de personnes qui l’ont comme ami moins le
nombre de personnes qu’elle a elle-même comme ami. Dans l’exemple VL, ’Amine’ a 3 personnes
comme amies et 2 personnes l’ont comme ami, son score est donc de -1 (2-3).
Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction score qui prend
en entrée un réseau res et renvoie un dictionnaire qui associe à chaque personne de res son score.
>>> scores(VL)
{’Amine’: -1,
’Barbara’: 0,
’Charles’: 1,
’Daniel’: 1,
’Emilie’: 0,
’Frank’: -1}

Réponse

def scores(r):
""" Reseau -> dict[str:int]"""

return {p: len(amis_avec(r,p)) - nb_amis(r, p) for p in r}

UPMC
c - Informatique - 1I001 - page 7 -
Question 2.4 : [4/66]
Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction relations qui
prend en entrée un réseau res et un ensemble pers de personnes de res et renvoie le dictionnaire
correspondant au sous-réseau de res composé des personnes de pers uniquement ; c’est à-dire le
réseau contenant uniquement les personnes pers et les même relations d’amitiés que dans res.
>>> relations(VL, {’Amine’, ’Barbara’, ’Daniel’})
{’Daniel’: {’Barbara’},
’Barbara’: {’Daniel’, ’Amine’},
’Amine’: {’Daniel’, ’Barbara’}}
>>> relations(VL, {’Charles’, ’Emilie’})
{’Charles’: set(), ’Emilie’: set()}
>>> relations(VL, set())
dict()

Réponse

def relations(reseau, ensemble):


""" Reseau * set[str] -> Reseau
H: pour tout e, e in ensemble => e in reseau"""

return {p : reseau[p]& ensemble for p in reseau if p in ensemble}

Dans un second temps, on modélise un réseau social professionnel comme un dictionnaire (de
type dict[str:tuple[str,str,set[str]]]) où les clefs sont des personnes, et les valeurs
associées sont des triplets (tuple[str, str, set[str]]) représentant
i) l’entreprise actuelle de la personne (par exemple ’COGIP’),
ii) le poste qu’occupe actuellement la personne (par exemple ’commercial’),
iii) l’ensemble des contacts de cette personne, c’est-à-dire les noms des personnes du réseau
professionnel qu’elle connaît, un set[str] (par exemple {’Bob’, ’Carole’,’Etienne’}).
Cette dernière relation n’est pas forcément réciproque.
On utilise l’alias de type ReseauPro :
# type ReseauPro = dict[str:tuple[str,str,set[str]]]

On suppose que, dans un ReseauPro, les ensembles présents en troisième position dans les valeurs
(les ensembles de contacts) ne contiennent que des éléments qui sont des clefs du réseau. On ne
peut pas avoir dans son ensemble de contacts une personne qui n’est pas une clef du réseau.
Pour les tests, on considère le réseau suivant :
LD = {’Alice’ : (’COGIP’, ’commercial’, {’Bob’, ’Carole’, ’Etienne’}),
’Bob’ : (’COGIP’, ’directeur’, {’Alice’}),
’Carole’ : (’COFRAP’, ’commercial’, {’Alice’, ’Bob’, ’Fanny’}),
’Damien’ : (’SOGEREC’, ’commercial’, {’Carole’}),
’Etienne’ : (’COGIP’, ’commercial’, {’Alice’, ’Bob’}),
’Fanny’ : (’COFRAP’, ’remouleur’, {’Carole’})}

Question 2.5 : [4/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction metiers qui
prend en entrée un réseau professionnel res et renvoie un dictionnaire dont les clefs sont les

UPMC
c - Informatique - 1I001 - page 8 -
postes présents dans res et la valeur associée à un poste est le nombre de personnes de res qui
occupent actuellement ce poste.
>>> metiers(LD)
{’directeur’: 1, ’commercial’: 4, ’remouleur’: 1}
>>> metiers(dict())
dict()

Réponse

def metiers(reseau):
""" ReseauPro -> dict[str:int] """

# DR : dict[str:int]
D = dict()

# k : str, entr : str, job : str, cont : set[str]


for (k, (entr, job, cont)) in reseau.items():
if job in D:
D[job] = D[job] + 1
else:
D[job] = 1
return D

Question 2.6 : [5/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction entreprises_amies
qui prend en entrée un réseau, deux entreprises différentes et renvoie True s’il existe dans le réseau
une personne appartenant à une des deux entreprises qui possède dans ses contacts une personne
appartenant à l’autre entreprise, et False sinon.
>>> entreprises_amies(LD, ’COGIP’, ’COFRAP’)
True
>>> entreprises_amies(LD, ’COFRAP’, ’SOGEREC’)
True
>>> entreprises_amies(LD, ’COGIP’, ’SOGEREC’)
False
>>> entreprises_amies(dict(), ’COGIP’, ’COFRAP’)
False

Réponse

def entreprises_amies(reseau, ea, eb):


""" ReseauPro * str * str -> bool
H: ea != eb"""

for (k1, (e1, job1, cont1)) in reseau.items():


if (e1 == ea) or (e1 == eb):
for k2 in cont1:
e2, job2, cont2 = reseau[k2]
if {ea, eb} == {e1, e2}:
return True
return False

UPMC
c - Informatique - 1I001 - page 9 -
UPMC
c - Informatique - 1I001 - page 10 -
Exercice 3 : Ensemble de Mandelbrot
Dans tout l’exercice, on considérera que l’import de la bibliothèque math a été fait au préalable.

Un point du plan est représenté par un couple de nombres flottants représentant son abscisse et
son ordonnée. On utilise l’alias de type Point :
# type Point = tuple[float,float]

Question 3.1 : [2/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction module qui,
étant donné un point pt, de coordonnées
p (x, y), renvoie son module.
Rappel : le module de pt vaut x2 + y 2 .
>>> module((3.0, 4.0))
5.0
>>> module((-2.0, 0.0))
2.0

Réponse

def module(pt):
""" Point -> float """

# x:float, y:float
x,y = pt
return math.sqrt(x**2 + y**2)

Pour chaque point de coordonnées (a, b), on définit la suite (de points (xn , yn )n∈N ) suivante :

 x0 = y0 = 0
xn+1 = x2n − yn2 + a (1)
yn+1 = 2xn yn + b

Question 3.2 : [2/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction suivant qui,
étant donné deux points c et pt, de coordonnées respectives (a, b) et (xn , yn ), renvoie le point de
coordonnées (xn+1 , yn+1 ).
>>> suivant((0.0, 0.0), (1.0, 2.0))
(-3.0, 4.0)
>>> suivant((3.0, -4.0), (1.0, 2.0))
(0.0, 0.0)

UPMC
c - Informatique - 1I001 - page 11 -
Réponse

def suivant(c, pt):


""" Point**2 -> Point """

# a:float, b:float
a,b = c
# x:float, y:float
x,y = pt
return (x**2 - y**2 + a, 2*x*y + b)

Question 3.3 : [4/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction est_bornee
qui, étant donné un point c, un entier nb_max et un nombre flottant mod_max, renvoie le booléen
True si les suites (xn )n∈N et (yn )n∈N sont telles que les points (xn , yn ) ont tous un module
strictement inférieur à mod_max pour l’ensemble des entiers n ∈ {0, 1, . . . , nb_max}. La fonction
renvoie False dans le cas contraire.
>>> est_bornee((0.0, 0.0), 10, 1.0)
True
>>> est_bornee((3.0, 4.0), 2, 5.0)
False

Réponse

def est_bornee(c, nb_max, mod_max):


""" Point * int * float -> bool
hypothese : nb_max >= 0 """

# i : int compteur du nb d’iterations


i = 0
# pt : point point courant
pt = (0.0, 0.0)
while ( module(pt)<mod_max and i<nb_max ):
pt = suivant(c, pt)
i = i+1

return module(pt)<mod_max

Étant donné debut, la borne inférieure d’un intervalle de longueur lg et un entier nb_pas, on
souhaite découper l’intervalle en nb_pas sous-intervalles. Par exemple, si on découpe l’intervalle
donné par debut = 2 et lg = 4, c’est-à-dire l’intervalle [2, 6], avec nb_pas = 4, on obtient
les 4 sous-intervalles [2, 3]; [3, 4]; [4, 5] et [5, 6].

On représente le résultat du découpage en la liste des coordonnées des extrémités des sous-
intervalles : [2, 3, 4, 5, 6].
Par ailleurs, le même intervalle peut-être découpé avec nb_pas = 5, on obtient alors la liste
[2, 2.8, 3.6, 4.4, 5.2, 6].

UPMC
c - Informatique - 1I001 - page 12 -
Question 3.4 : [2/66]
Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction decoupage qui,
étant donné deux nombres flottants debut et lg et un entier nb_pas, renvoie la liste des coor-
données du découpage.
>>> decoupage(2.0, 4.0, 4)
[2.0, 3.0, 4.0, 5.0, 6.0]
>>> decoupage(-1.0, 4.0, 8)
[-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]

Remarque : Un bonus de 2 points sera accordé si la réponse est donnée avec une compréhension.
Réponse

def decoupage(debut, lg, nb_pas):


""" float * float * int -> list[float]
hypotheses : lg > 0 and nb_pas > 0 """

# pas : float pas entre deux flottants consecutifs


pas = lg / nb_pas

return [debut + i*pas for i in range(nb_pas+1)]

La donnée d’un point coin et d’une longueur lg, définit un unique carré, dont le coin inférieur
gauche est le point coin, dont les côtés, de longueur lg, sont parallèles aux axes.
Ainsi pour le point coin = (2,1) de lg = 2, on définit le carré de sommets (2, 1); (4, 1); (4, 3)
et (2, 3).

On construit un ensemble de points, espacés de façon régulière sur le carré avec un nombre de pas
défini par l’entier nb_pas. Cet ensemble de point est appelé maillage. Sur la figure suivante, on
a représenté à gauche le maillage avec coin = (2.0, 1.0), lg = 2.0 et nb_pas = 2 et à
droite, le maillage avec coin = (1.0, 1.0), lg = 2.0 et nb_pas = 8.

Figure 1 – Maillage de deux carrés.

UPMC
c - Informatique - 1I001 - page 13 -
Question 3.5 : [3/66]
Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction
maillage(coin, lg, nb_pas) qui renvoie l’ensemble des points du maillage.
>>> maillage((2.0, 1.0), 2.0, 2)
{(3.0, 2.0), (3.0, 3.0), (3.0, 1.0), (2.0, 1.0), (2.0, 3.0), \
(4.0, 3.0), (2.0, 2.0), (4.0, 2.0), (4.0, 1.0)}

Réponse

def maillage(coin, lg, nb_pas):


""" Point * float * int -> Set[Point]
hypotheses : lg > 0 and nb_pas > 0 """

# debut_x:float debut_y:float
debut_x, debut_y = coin

# dec_x:list[float] dec_y:list[float]
dec_x = decoupage(debut_x, lg, nb_pas)
dec_y = decoupage(debut_y, lg, nb_pas)

return { (x,y) for x in dec_x for y in dec_y }

L’ensemble de Mandelbrot est un ensemble de points du plan formant une fractale. Sur la figure
suivante, on visualise deux approximations d’une partie de la fractale.

Figure 2 – Approximations de l’ensemble de Mandelbrot.

Afin d’approximer l’ensemble de Mandelbrot, on retient chaque point c d’un maillage si la suite
associée, définie par l’équation (1), avant la question 2 est bornée.

Question 3.6 : [3/66]


Donner une définition avec signature et hypothèse(s) éventuelle(s) de la fonction
mandelbrot(coin, lg, nb_pas, nb_max, mod_max)

UPMC
c - Informatique - 1I001 - page 14 -
qui renvoie l’ensemble des points du maillage appartenant à l’ensemble de Mandelbrot.
Sur la Figure 2, on a représenté, à gauche, l’ensemble de points obtenus via l’appel
mandelbrot((-1.0,-1.0), 2.0, 300, 12, 2). À droite, on a représenté les points de
mandelbrot((0.3,0.45), 0.1, 200, 30, 2).
Sur un maillage plus petit, on obtient par exemple
>>> mandelbrot((-0.5,-1.0), 1.0, 2, 12, 2)
{(-0.5, -0.5), (-0.5, 0.0), (0.0, -1.0), (0.0, -0.5), (0.0, 0.0)}

Réponse

def mandelbrot(coin, lg, nb_pas, nb_max, mod_max):


""" Point * float * int * int * float -> Set[Point]
hypotheses : lg > 0 and nb_pas > 0 """

return { c for c in maillage(coin, lg, nb_pas) \


if est_bornee(c, nb_max, mod_max)}

Exercice 4 : Analyse de Code


Voici le code d’une fonction max_et_reste qui extrait le maximum d’une liste (c’est-à-dire qui
sépare un élément maximum du reste de la liste). En effet, max_et_reste prend en entrée une
liste L et renvoie un couple composé du maximum de L, et d’une liste contenant tous les éléments
de L, sauf une occurrence du maximum ; ces éléments peuvent se trouver dans un ordre différent
de celui de L.

UPMC
c - Informatique - 1I001 - page 15 -
def max_et_reste(L):

""" list[Number] ->

Hyp:
renvoie le maximum de la liste L et une liste contenant
tous les elements de L moins une occurrence du maximum"""

# max :

max = L[0]

# LR :

LR = []

# e :

for e in L[1:]:
if e > max:
LR.append(max)
max = e
else:
LR.append(e)
return (max, LR)

Question 4.1 : [1/66]


Compléter la spécification et les informations de type dand la définition de max_et_reste ci-
dessus.

UPMC
c - Informatique - 1I001 - page 16 -
Réponse

list[Number].
def max_et_reste(L):
""" list[Number] -> tuple[Number, list[Number]]
H: len(L) > 0
renvoie le maximum de L et une liste contenant
tous les elements de L sauf une occurence du maximum"""

# max : Number
max = L[0]
# LR : list[Number]
LR = []

# e : Number
for e in L[1:]:
if e > max:
LR.append(max)
max = e
else:
LR.append(e)
return (max, LR)

UPMC
c - Informatique - 1I001 - page 17 -
Question 4.2 : [2/66]
Donner un jeu de trois tests caractéristiques pour max_et_reste.
Remarque : Il est probablement nécessaire d’effectuer une ou plusieurs simulations (sur un
brouillon) pour comprendre le fonctionnement de max_et_reste et pour donner des tests perti-
nents. On ne demande pas de faire figurer ces simulations sur la copie.

Réponse

Des cas de tests pertinents :


— liste "dans le désordre" d’au moins 3 éléments avec un seul maximum,
— liste "dans le désordre" avec plusieurs occurrences du maximum,
— liste triée dans l’ordre décroissant,
— liste triée dans l’ordre croissant,
— liste composée d’un seul élément, . . .
Remarque : vérifier que les listes résultats sont bien dans l’ordre induit par la fonction (qui
peut différer de l’ordre initial).
assert max_et_reste([2, 3, 1, 4]) == (4, [2, 1, 3])
assert max_et_reste([3, 4, 4, 3, 2]) == (4, [3, 4, 3, 2])
assert max_et_reste([4, 4, 3, 2, 1, 0]) == (4, [4, 3, 2, 1, 0])
assert max_et_reste([1, 2, 3, 4, 5]) == (5, [1, 2, 3, 4])
assert max_et_reste([3]) == (3, [])

UPMC
c - Informatique - 1I001 - page 18 -
On se sert de la fonction max_et_reste pour écrire une fonction tri_sel (pour "tri par sé-
lection") qui prend en entrée une liste L et renvoie une liste correspondant à L triée par ordre
décroissant.

def tri_sel(L):

""" list[Number] ->


renvoie la liste L triée dans l’ordre décroissant"""

# LR :
LR = []

# T :
T = L[:]

# paire :
paire = (0, [])

while T != []:
paire = max_et_reste(T)

#g : , d :
g, d = paire
LR.append(g)
T = d

return LR

Question 4.3 : [2/66]


Compléter la spécification et les informations de type dans la définition de tri_sel ci-dessus.

UPMC
c - Informatique - 1I001 - page 19 -
Réponse

def tri_sel(L):
""" list[Number] -> list[Number]
renvoie la liste L triee dans l’ordre decroissant"""

# LR : list[Number]
LR = []
# T : list[Number]
T = L[:]
# paire : tuple[Number, list[Number]]
paire = (0, [])

while T != []:
paire = max_et_reste(T)
#g : Number, d : list[Number]
g, d = paire
LR.append(g)
T = d

return LR

Question 4.4 : [3/66]


Compléter la table de simulation ci-dessous pour l’application :
tri_sel([5, 3, 2, 1, 4])
Remarque : la table comporte plus de colonnes et de lignes que nécessaire.

Tour Variable Variable Variable Variable Variable

entrée

1er

Réponse

Simulation pour : tri_sel([5, 3, 2, 1, 4])

UPMC
c - Informatique - 1I001 - page 20 -
Tour Variable paire Variable LR Variable T

entrée (0, []) [] [5, 3, 2, 1, 4]

1er (5, [3, 2, 1, 4]) [5] [3, 2, 1, 4]

2eme (4, [2, 1, 3]) [5,4] [2, 1, 3]

3eme (3, [1, 2]]) [5, 4, 3] [1, 2]

4eme (2, [1]]) [5, 4, 3, 2] [1]

5eme (S) (1, []) [5, 4, 3, 2, 1] []

>>> tri_sel([5, 3, 2, 1, 4])


[5, 4, 3, 2, 1]

Question 4.5 : [4/66]


On s’intéresse à la correction de tri_sel et, pour ce fait, on cherche un invariant de boucle pour
cette fonction. On liste les propositions suivantes :
1. "la partie gauche de paire est plus grande que tous les éléments de la partie droite de
paire."
2. "les éléments de L sont exactement les éléments de paire et la partie droite de paire est
triée par ordre décroissant."
3. "LR est triée par ordre décroissant et les éléments de L sont exactement les éléments de LR
et de T."
4. "Le dernier élément de LR est plus grand que tous les éléments de T."
P+∞
5. "la partie gauche de paire vaut i=0 ai cos(bi πx)."
Une seule de ces propositions est un invariant de boucle pertinent.
Trouver cette proposition, vérifier qu’elle est un invariant sur la simulation précédente (c’est à
dire, calculer l’invariant pour chaque ligne du tableau), et expliquer comment, quand on sort de
la boucle, on peut déduire la correction de tri_sel de cet invariant.

UPMC
c - Informatique - 1I001 - page 21 -
Réponse

La proposition 3. est invariant de boucle pertinent.


Lors de la simulation précédente, [], [5], [5, 4], [5, 4, 3], [5, 4, 3, 2] et
[5, 4, 3, 2, 1] sont bien triées par ordre décroissant.
Deplus, les élements de L qui vaut [5, 3, 2, 1, 4] sont bien les éléments de i) [] et
[5, 3, 2, 1, 4], ii) de [5] et [3, 2, 1, 4], iii) de [5, 4] et [2, 1, 3], iv) de
[5, 4, 3] et [1, 2], v) de [5, 4, 3, 2] et [1], et vi) de [5, 4, 3, 2, 1] et []
Quand on sort de la boucle a) l’invariant est vrai, b) la condition de boucle est fausse.
C’est à dire :
a) LR est triée par ordre décroissant et les éléments de L sont exactement les éléments de LR et
de T
b) T = []
On en déduit :
LR est triée par ordre décroissant et les éléments de L sont exactement les éléments de LR.
Ce qui signifie :
LR est L triée par ordre décroissant. Comme la fonction tri_sel renvoie LR, elle est correcte.

Question 4.6 : [2/66]


On s’intéresse à la terminaison de tri_sel. Trouver un variant qui garantit que tri_sel ter-
mine.

Réponse

len(T) est un bon variant pour tri_sel, il décroit strictement à chaque tour de boucle (car
la longueur de T est décrémentée) et on sort de la boucle quand il vaut 0 (car T vaut []).

UPMC
c - Informatique - 1I001 - page 22 -
Question 4.7 : [0/66]
On s’intéresse à l’efficacité de tri_sel.
Lors d’un appel sur une liste de n éléments, combien de comparaisons > sont faites par la fonction
max_et_reste ?
En déduire combien de comparaisons > sont faites lors d’un appel à la fonction tri_sel sur une
liste de n éléments.
Remarque : Cette question, hors-programme, est notée sur 3 points bonus.

Réponse

Appelée sur une liste L, la fonction max_et_reste effectue une comparaison > pour chaque
élément de L[1:]. Elle effectue donc n − 1 comparaisons > si L a la taille n.
La fonction tri_sel appelle max_et_reste sur T à chaque tour de boucle et fait donc, à
chaque tour, autant de comparaisons que len(T)-1.
Quand on appelle tri_sel sur une liste L de taille n, au premier tour, comme T vaut L, on
effecture n − 1 comparaisons > ; au deuxième tour, T a un élément en moins et est donc de taille
n − 1, on effectue donc n − 2 comparaisons ; au troisième tour, on effectue n − 3 comparaisons ;
. . ., au dernier tour, T a la taille 1, on effectue 0 comparaisons.
Au final, le nombre de comparaisons > effectuées est :
n−1
X n ∗ (n − 1)
(n − 1) + (n − 2) + (n − 3) + . . . + 0 = k=
2
k=0

UPMC
c - Informatique - 1I001 - page 23 -

Vous aimerez peut-être aussi