1 Exponentiation de grands entiers

publicité
Université de Nice-Sophia Antipolis
Algorithmique & Programmation
Deug MIAS-MI 1
2002–2003
TP N 5
Analyse des performances
Buts :
– Analyser les performances d’un programme.
– Etre capable de comprendre la complexité d’un algorithme.
1 Exponentiation de grands entiers
Le but de cette section est de réaliser des fonctions permettant de calculer des puissances
entières de grands entiers. Les grands entiers existent déjà en Java, il s’agit du type long qui
et
permet de représenter des valeurs entières signées comprises entre (c’est à dire entre et , donc codées sur 64 bits) 1 . Vous allez
implémenter deux algorithmes pour le calcul de , où est un entier de type long et
un
entier de type int.
1.1 Première méthode : algorithme naïf
a. Écrivez une fonction myPow1 qui prend en argument un entier positif de type long et un
entier
positif de type int, et qui calcule ! suivant l’algorithme ci-dessous.
fonction myPow1 (n : entier_long, m : entier) : entier_long
variables
res : entier_long "
1
i : entier
début
pour i de 1 à m faire
res "%# $
finpour
retourner res
n
fin
Il est facile de constater que cet algorithme a une complexité en
sages dans la boucle et une opération par passage.
&(')+* . En effet, il y a
pas-
b. Écrivez une méthode main dans votre classe pour tester votre fonction (n’oubliez pas de
prévoir les erreurs de saisie).
1.2 Seconde méthode : algorithme paresseux
c. Écrivez une fonction myPow2 qui prend les mêmes arguments que la fonction précédente,
mais qui suit l’algorithme ci-dessous.
1. Il existe également la classe java.math.BigInteger qui permet de gérer des entiers de taille quelconque et donc
encore plus longs. Nous ne la manipulerons pas lors de cette séance.
1
fonction myPow2 (n : entier_long, m : entier) : entier long
variables
m
cmpt : entier "
a : entier_long "
1
b : entier_long "
n
début
tantque cmpt > 1 faire
si cmpt est pair alors
a
"%# $
b
finsi
b "%# $ b
cmpt
fintantque
retourner a "%$
2
b
fin
Proposition 1 Cet algorithme calcule avec une complexité en
&('
*.
Preuve Montrons d’abord la terminaison de l’algorithme 2 . Nous pouvons remarquer que (cmpt)
constitue une suite décroissante d’entiers strictement positifs puisque lors de chaque itération
cmpt est divisé par 2. La condition d’arrêt de la boucle sera donc bien atteinte et cela garantit que
l’algorithme se termine.
Montrons maintenant la correction de l’algorithme, c’est-à-dire qu’il calcule bien . Cela va
se faire par récurrence grâce à l’invariant de boucle suivant : à chaque itération, cmpt .
Ceci se montre par récurrence :
– Initialisation : au début, par définition, cmpt .
– On note par , et les valeurs de a, b et cmpt à l’itération . Supposons alors que
et que . Alors, on a , et !" # %$ . On
&( ' )(*
en déduit que ' * %+ ,- %$ , qui est bien égal à d’après
l’hypothèse de récurrence. Ainsi, la correction de l’algorithme est prouvée.
Il ne reste plus qu’à déterminer la complexité de l’algorithme. On remarque qu’à chaque itération, cmpt est divisé par deux jusqu’à ce qu’il soit inférieur ou égal à 1. Comme initialement il
itérations. Ceci prouve donc le résultat annoncé.
vaut , on en déduit qu’on fait au plus d. Maintenant que vous êtes persuadés que cet algorithme fait bien ce qui était attendu, testez-le
dans votre méthode main.
1.3 Aspects pratiques de la complexité
e. Ajoutez un compteur dans chacune des fonctions myPow1 et myPow2 qui compte le nombre
d’opérations arithmétiques et de comparaisons effectuées par vos fonctions. Ainsi, ces fonctions
retourneront un tableau de deux entiers contenant le résultat de l’algorithme et le nombre d’opé
rations qu’il a dû faire pour cela. Calculez avec chacune des fonctions pour = 10, 20, 30, 40,
50. Comparez le nombre d’opérations faites par chaque fonction et tirez-en les conclusions qui
s’imposent.
2 Recherches dans un tableau
Le but de cette section est de comparer l’efficacité des algorithmes de recherche séquentielle
et dichotomique d’une valeur dans un tableau trié de nombres entiers.
2. La terminaison d’un algorithme dénote le fait que l’algorithme se termine, notamment qu’il n’est pas piégé dans
une boucle infinie.
2
2.1 Recherche séquentielle et recherche dichotomique
f. Ecrivez une fonction rechSequentielle (cf. cours) qui prend en argument un tableau d’en-
tier tab trié et un nombre entier positif n. Cette fonction renvoie la position de n s’il est trouvé
dans le tableau et renvoie -1 sinon.
g. Ecrivez maintenant une fonction rechDichotomique (cf. cours) qui prend les mêmes arguments que la fonction précédente et renvoie la même valeur, mais qui se base sur l’idée de
dichotomie (diviser pour régner) pour effectuer la recherche.
h. Quelle est la complexité de ces deux fonctions?
i. Écrivez une méthode main permettant de tester vos fonctions. Cette méthode, doit créer un
tableau de 1 000 000 de nombres entiers dont chaque case tab[i] sera initalisée avec la valeur
i + i/2, ce qui permet d’obtenir un tableau trié. Vous lancerez la recherche d’un nombre appartenant au tableau (par exemple 1 125 000) et d’un nombre n’appartenant pas au tableau (par
exemple 1 125 002) afin de vérifier la correction de vos deux fonctions.
2.2 Temps d’exécution
j. Afin de comparer les temps d’exécution des deux fonctions, implantez la classe Time dont le
code vous est fournit ci-dessous. Cette classe possède un constructeur qui créé un objet contenant la date actuelle et une fonction d’instance millisecondsFrom qui renvoie le nombre de
millisecondes séparant deux objets de type Time. Elle va nous permettre de mesurer les temps
d’exécution des recherches.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Time
{
private java.util.Date date;
Time()
{
date = new java.util.Date();
}
long millisecondsFrom(Time t2)
{
return (date.getTime() - t2.date.getTime());
}
}
k. Les ordinateurs que nous utilisons étant très rapides, vous créerez deux boucles for réalisant
chacune 1 000 itérations. Dans la première, vous lancerez la recherche séquentielle d’une valeur,
et dans la seconde, vous lancerez la recherche dichotomique de la même valeur (afin que les tests
soient équitables).
Afin de mesurer le temps d’exécution de chaque boucle, vous créerez un objet de type Time
avant et après la boucle, et vous mesurerez le nombre de millisecondes qui les sépare en appelant
la fonction millisecondsFrom.
l. Comparez les temps d’exécution des 1 000 recherches séquentielles et dichotomiques.
2.3 Nombre d’opérations réalisées
m. De la même manière que pour le premier exercice, rajoutez un compteur permettant d’évaluer le nombre de comparaisons effectuées par chaque méthode.
3
Téléchargement