Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

ds1-fevrier2018-cor

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 8

février 2018

ASD Licence Informatique -


Semestre 4
DS1 - documents de cours, TD, TP autorisés - durée 2h

Note : lorsque, dans une question, est demandée la complexité, c’est une fonction de la taille de la
donnée qui est attendue. Lorsque c’est le comportement asymptotique, la réponse attendue est soit
une notation O, soit une notation Θ soit une notation Ω (le choix vous incombe).
Vous pourrez utiliser toute fonction vue en cours, TD, TP à la condition expresse de spécifier correc-
tement les entrées et les sorties.
Le barême est donné à titre indicatif.
Livres, calculatrices et objets de communication interdits.

Exercice 1 : Questions de cours [2 points] (réponses sans justification)

Q 1.1 Si je prouve qu’un algorithme est en Θ(n2 ), peut-il être en Ω(n3 ) ? Répondre par oui ou non.
Corrigé

Non, puisqu’on ne peut prouver qu’il existe n0 et c tel que c × n3 < n2 ∀n ≥ n0 .

Q 1.2 Si je prouve qu’un algorithme est en Θ(n log n) dans le pire des cas, peut-il exister des cas en Ω(n2 ) ?
Répondre par oui ou non.
Corrigé

Non. Puisque l’algorithme s’exécute dans le pire des cas en Θ(nlogn), le meilleur des cas ne pourra pas
être en Ω(n2 ) puisque n log n < n2 .

Q 1.3 On peut résoudre l’équation de récurrence (qui compte les appels de f) de l’algorithme suivant en
utilisant le théorème général. Répondre par vrai ou faux.
def f ( t ):
if t == []:
return 0
else :
return 1 + 2 * f ( t [0: len ( t )//4])

Corrigé

Oui. L’algorithme divise la taille du problème à chaque appel. (on pourrait ajouter en un nombre constant
de sous-problèmes)

Q 1.4 c(n) = 5c( n5 ) + n


2 = Θ(n log n). Répondre par vrai ou faux. Corrigé

Vrai. Cas 2 du théorème général.

Exercice 2 : Complexité [4.5 points]


Compétence évaluée : calculer des complexités, définir pire et meilleur des cas.
On donne l’algorithme suivant, qui prend en entrée un tableau t de n Element (la classe Element est
rappelée à la fin de l’énoncé).
On suppose que n est pair.

1
n = len ( t )
s1 = Element (0)
s2 = Element (0)
fini = False
i = 0
while i <= n // 2 and not fini :
s1 . add ( t [ i ]) # il fallait lire s1 = s1 . add ( t [ i ])
s2 . add ( t [n -1 - i ]) # il fallait lire s2 = s2 . add ( t [n -1 - i ])
if s1 . cmp ( s2 ) == 0:
fini = True
i = i + 1

Q 2.1 Décrire un meilleur des cas pour le nombre d’additions d’Element (le nombre d’appels à la méthode
add).
Corrigé

Si t est tel que t[0] = t[n − 1] : la boucle est exécutée un seule fois, la comparaison retourne vraie et le
booléen passe à vrai.

Q 2.2 Donner exactement le nombre cm (n) d’additions d’Element pour un tableau de taille n pour le
meilleur des cas.
Corrigé

cm (n) = 2

Q 2.3 Décrire un pire des cas pour le nombre d’additions d’Element. Puis donner un exemple pour
n = 10.
Corrigé
Pj Pj
Si t est tel que pour tout j ∈ [0, n//2], i=0 t[i] 6= i=0 t[n − 1 − i] : la boucle est exécutée n//2 fois. Par
exemple [0,1,1,1,1,1,1,1,1,1].

Q 2.4 Donner exactement le nombre cp (n) d’additions d’Element pour un tableau de taille n pour le pire
des cas. Justifier.
Corrigé
P n2
cp (n) = i=0 2 = 2 × ( n2 + 1)

Q 2.5 Donner le comportement asymptotique de cet algorithme pour le nombre d’additions d’Element.
Corrigé

Dans le meilleur des cas en Θ(1). Dans le pire des cas en Θ(n).

Q 2.6 Donner le comportement asymptotique de cet algorithme pour le nombre de comparaisons d’Element
(le nombre d’appels à la méthode cmp). Justifier.
Corrigé

On est en O(n) et Ω(1). Les pire et meilleur des cas sont les mêmes que pour le nombre d’additions.

2
Exercice 3 : Revisite du tri bulle [6.5 points]
Compétence évaluée : comprendre un nouvel algorithme, analyser des résultats expérimentaux.
L’algorithme du tri bulle est souvent présenté avec une double boucle pour aboutissant à une complexité
en nombre de comparaisons en Θ(n2 ) (comme vu en cours).
Voici une version un peu modifiée :
def bubbleSort ( t ):
"""
: param t : numpy array of Element
"""
fini = False
indices = range ( len ( t ) -1)
while not fini :
echange = False
for i in indices :
if cmp ( t [ i ] , t [ i +1]) > 0:
t [ i ] , t [ i +1] = t [ i +1] , t [ i ]
echange = True
if not echange :
fini = True

Q 3.1 Décrire un meilleur des cas en nombre de comparaisons (le nombre d’appels à la fonction cmp).
Corrigé

Si le tableau est trié en ordre croissant, aucun échange n’est réalisé, la boucle tant que ne fait qu’un tour.
La boucle pour est exécutée une fois, avec une comparaison à chaque fois.

Q 3.2 Quel est le nombre exact de comparaisons dans le meilleur des cas.
Corrigé
Pn−1
i=1 1=n−1

Q 3.3 Décrire un pire des cas.


Corrigé

Si le tableau est trié en ordre décroissant, au moins un échange est réalisé pour chaque élément, la boucle
tant que est réalisée n fois.

Q 3.4 Quel est le nombre exact de comparaisons dans le pire des cas.
Corrigé
Pn Pn−1 Pn
i=1 j=1 1= i=1 n − 1 = n(n − 1)

3
On propose maintenant une amélioration 1 :
def cocktailSort ( t ):
fini = False
indices = range ( len ( t ) -1)
while not fini :
echange = False
for i in indices :
if cmp ( t [ i ] , t [ i +1]) > 0:
t [ i ] , t [ i +1] = t [ i +1] , t [ i ]
echange = True
if not echange :
fini = True
if not fini :
echange = False
for i in reversed ( indices ):
if cmp ( t [ i ] , t [ i +1]) > 0:
t [ i ] , t [ i +1] = t [ i +1] , t [ i ]
echange = True
if not echange :
fini = True

Q 3.5 Le meilleur des cas est-il modifié ? Expliquez pourquoi.


Corrigé

Ce n’est pas modifié. En effet, comme il n’y a pas d’échange à la première boucle pour, fini devient vrai
et la seconde boucle pour n’est pas exécutée.

Q 3.6 Le pire des cas est-il modifié ? Expliquez pourquoi.


Corrigé

Ce n’est pas modifié non plus. La première boucle pour place correctement l’élément le plus grand. La
seconde boucle pour place correctement l’élément le plus petit. Et ainsi de suite, à chaque tour de boucle
tant que, deux éléments sont bien placés.

La courbe ci-dessous montre le nombre moyen de comparaisons réalisé sur un tirage aléatoire de 100
tableaux pour chaque longueur entre 1 et 100, pour le bubbleSort et pour le coctailSort.
1. On trouve différentes dénominations de ce tri : cocktailSort ou shakerSort

4
Q 3.7 Qu’en conluez-vous sur l’efficacité du cocktailSort par rapport au bubbleSort ?
Corrigé

En moyenne le cocktailSort est plus rapide que le bubbleSort. La complexité en moyenne semble quadra-
tique.

Q 3.8 Donner un exemple d’un tableau de taille 5 où le coctailSort est plus efficace (vous déroulerez les
étapes de chacun des deux tris en affichant le contenu du tableau après chaque boucle pour).
Corrigé

Prenons le tableau [5 2 3 4 1], l’exécution des deux tris donne les résultats suivants :

bubbleSort coctailSort

[2 3 4 1 5] [2 3 4 1 5]
[2 3 1 4 5] [1 2 3 4 5]
[2 1 3 4 5] [1 2 3 4 5]
[1 2 3 4 5] nb cmp = 12
[1 2 3 4 5]
nb cmp = 20

Exercice 4 : Calcul des deux points les plus proches [7 points]


Compétence évaluée : comprendre un algorithme décrit en langage naturel, établir l’équation
de récurrence donnant la complexité d’un algorithme récursif, appliquer le théorème général.
Nous souhaitons concevoir un algorithme qui, étant donné un ensemble de points dans le plan, est capable
de trouver les deux points les plus proches (au sens de la distance euclidienne).
Un algorithme naı̈f consiste à prendre chaque point, puis à calculer sa distance par rapport aux autres
points, et à conserver les deux points dont la distance est minimale.
Les points seront représentés par des couples en Python et l’ensemble des points sera représenté par une
liste. On suppose donnée une fonction distance qui prend deux points en entrée et calcule la distance qui
les sépare en temps constant.
5
Q 4.1 Ecrire en Python la fonction suivante :
def p o i n t _ l e s _ p l u s _ p r o c h e s _ n a i f ( l ):
"""
Calcule les deux points les plus proches de la liste l .

: param l : liste des points


: type l : List de couples de float

: return : les deux couples correspondant aux points les plus proches

CU : la liste contient au moins 2 points


"""

Corrigé

def p o i n t _ l e s _ p l u s _ p r o c h e s _ n a i f ( l ):
n = len ( l )
r1 , r2 = l [0] , l [1]
min = distance ( r1 , r2 )
i = 0
for p1 in l :
i += 1
for p2 in l [ i +1: n ]:
d = distance ( p1 , p2 )
if d < min :
r1 , r2 = p1 , p2
min = d
return r1 , r2

Q 4.2 Quel est le nombre de calculs de distances entre deux points réalisé par votre algorithme naı̈f donné
à la question précédente si l’ensemble des points est de taille n ?
Corrigé
Pn Pn Pn n(n−1)
1+ i=1 j=i+1 1=1+ i=1 n−i=1+ 2

On présente maintenant une autre stratégie. On ne vous demande pas de comprendre pourquoi cela
fonctionne dans le cadre de cet examen, car la preuve de l’algorithme n’est pas évidente.
On supposera que la liste des points ` est triée selon les abscisses des points (cela ne rentre pas en compte
dans la complexité de l’algorithme). Puis on applique l’algorithme suivant récursivement :
Etape 1 diviser ` en deux : `1 contient les bn/2c premiers points 2 , `2 contient les dn/2e derniers points
Etape 2 trouver récursivement les deux points les plus proches dans `1 et dans `2 , notons respectivement
d1 et d2 les distances de ces deux paires de points, on note d = min(d1 , d2 )
1 2
Etape 3 on considère l’abcisse xm = ` [−1][0]+`
2
[0][0]
, construire la liste s des points de ` dont les abscisses
sont au maximum à une distance d de l’abscisse xm
Etape 4 trier s selon l’axe des ordonnées
Etape 5 trouver les deux points les plus proches dans s (en utilisant l’algorithme naı̈f ). Soit ds la
distance entre ces points. Si s est vide, ds := d.
Etape 6 retourner le minimum entre d et ds
2. ceux d’abscisse la plus faible étant donné que ` a été triée selon les abscisses

6
dx dx

d1

ds

d2

xm

Q 4.3 Quel nom donne-t-on à la stratégie mise en place dans cet algorithme ?
Corrigé

Diviser pour régner.

Q 4.4 Quel tri choisir pour les tris de listes dans l’algorithme ? Justifier ce choix par des considérations
sur la complexité de votre algorithme.
Corrigé

Tout tri qui est en O(n log n) en temps.

Q 4.5 L’algorithme décrit n’indique pas quand la récursion s’arrête. Décrire en français le ou les cas de
base.
Corrigé

Si la liste est de longueur 2 ou 3.

Q 4.6 Quel est le nombre d’appels à la fonction distance à l’étape 5 dans le pire des cas ? Justifier en
décrivant le pire des cas.
Corrigé

n(n−1)
Dans le pire des cas, tous les points sont dans s. 1 + 2

Q 4.7 Donner l’équation de récurrence de l’algorithme récursif dans le pire des cas ?
Corrigé

Dans le pire des cas, tous les points sont dans s. Donc l’équation de récurrence s’écrit :

 1 si `est de longueur 2
c(n) = 2 si `est de longueur 3
n n(n−1)
2c( 2 ) + 1 + 2 sinon

Q 4.8 En déduire la complexité asymptotique de l’algorithme récursif dans le pire des cas. Justifier.
Corrigé

a = 2, b = 2, logb a = 1, f (n) = 1 + n(n−1)


2 f (n) = Ω(n1 ), on est dans le cas 3 du théorème général. On
n/2(n/2−1) 1 n(n−1)
vérifie que 2(1 + 2 ) ≤ 2 (1 + 2 ). Donc c(n) = Θ(f (n)) = Θ(n2 ).
7
En cherchant à déterminer le meilleur des cas, un étudiant propose ceci :  Pour l’étape 5 le meilleur
des cas est lorsque l’ensemble s est vide, il n’y a jamais de calcul de distance et la complexité de l’algorithme
de recherche des deux points les plus proches dans le meilleur des cas est c(n) = 0 . Ses camarades lui
indiquent qu’il se trompe.

Q 4.9 Quelle erreur de raisonnement a commis cet étudiant ?


Corrigé

L’ensemble s ne peut pas toujours être vide sinon c’est qu’il n’y a pas de points.

import time
from functools import total_ordering

@total_ordering
class Element :

def __init__ ( self , value ):


assert ( type ( value ) == int )
self . value = value

def __add__ ( self , other ):


return Element ( self . value + other . value )

def __eq__ ( self , other ):


return self . value == other . value

def __ne__ ( self , other ):


return not ( self == other )

def __lt__ ( self , other ):


return self . value < other . value

def __repr__ ( self ):


return " {} " . format ( self . value )

Vous aimerez peut-être aussi