Vous êtes sur la page 1sur 3

Université d’Aix-Marseille - Faculté des Sciences

Licence d'Informatique - Licence de Mathématiques

ENSIN2U1 - Programmation

TP 6

Structures – Unions - Piles tabulaires

1. Algorithme ou “schéma” de Hörner.

L’algorithme dit de Hörner, permet de calculer efficacement la valeur d’un polynôme. Nous allons
étudier cet algorithme, ce qui passe déjà par la question de la représentation d’un polynôme.
Considérons un polynôme P(x) = an xn + a n-1xn-1 +… a2 x2 + a 1 x + a0 dont les coefficients ai sont des
réels.

1.1 Structure de données

Donnez une structure de données C qui permet de représenter un polynôme. Il faudra pouvoir
mémoriser les coefficients ai et également le degré du polynôme. L’usage d’un type struct semble
bien adapté…

1.2 Version naïve de l’évaluation de polynômes

Reprendre une fonction de calcul de puissance que vous avez déjà programmée et, en l’utilisant,
écrivez une fonction qui, étant donnés un polynôme P et un réel x, évalue P(x). Vous testerez votre
fonction en affichant, en plus du résultat, le nombre d’additions et de multiplications de valeurs réelles
nécessaires à cette évaluation. Intégrez ces fonctions dans un programme (les coefficients d’un
polynôme sont saisis au clavier) et testez-les.

1.3 L’approche de Hörner

On peut disposer d’une méthode de calcul plus rapide. Elle repose sur les constats suivants :

P(x) =
an xn + a n-1xn-1 +… a2 x2 + a 1 x + a0 =
( an xn-1 + a n-1xn-2 +… a2 x + a1 ).x + a0 =
( ( an xn-2 + a n-1xn-3 +… a3 x + a2 ).x + a1 ).x + a0 =
( ( ( an xn-3 + a n-1xn-4 +… a3 ).x + a2 ).x + a1 ).x + a0 =

( (… ( ( an ).x + an-1 ).x+… +… a3 ).x + a2 ).x + a1 ).x + a0

De cette façon là, on évite les calculs répétés de puissances. En utlisant cette propriété, écrivez une
fonction qui, étant donnés un polynôme et un réel x, évalue P(x). Vous testerez votre fonction en
affichant, en plus du résultat, les nombres d’additions et de multiplications de valeurs réelles
nécessaires à cette évaluation. Intégrez votre fonction dans un programme et testez-la. Vous
comparerez les nombres d’additions et de multiplications de valeurs réelles avec celles nécessaires
pour la version naïve.

1.4 L’approche de Hörner récursive

Programmez une version récursive de la méthode précédente.


2. Exploitation d’une pile tabulaire : évaluation d’expressions arithmétiques

Une pile peut servir à l’évaluation d’une expression arithmétique. Nous simplifierons ici le problème
en supposant d’une part n’avoir affaire qu’à des petits nombres en entrée, correspondant aux 10
chiffres (0 à 9) et à des expressions bien parenthésées qui seront rentrées dans une chaîne de
caractères (non vide) de la forme suivante ((6*(5-(1+2)))+3)=  où ‘ = ‘ précisera que
l’expression est terminée.

Pour évaluer cette expression, on va traiter chaque caractère de la suite en le lisant et en fonction de ce
qu’il représente, on adoptera des traitements adaptés :
- on empile pour le cas où le caractère est un chiffre ou un opérateur
- on procède au traitement suivant s’il s’agit d’une parenthèse fermante :
o on dépile l’élément au sommet (disons y),
o on dépile l’élément au sommet qui est un opérateur (disons op),
o on dépile l’élément au sommet (disons x),
o on réalise l’évaluation x op y
o on empile le résultat de l’évaluation précédente
- s’il s’agit d’une parenthèse ouvrante, on ne fait rien
- on dépile pour le cas où le caractère est ‘=’ ; la valeur dépilée est le résultat de l’évaluation

Pour l’exemple, on va empiler jusqu’au chiffre 2. La pile contiendra alors dans l’ordre [6*5-1+2].
Avec la parenthèse qui suit le chiffre 2, on dépile trois élements de sorte à obtenir x=1, op=+ et y=2.
On évalue le résultat 3, que l’on empile. On va ensuite dépiler trois élements de sorte à obtenir x=5,
op=- et y=3. On évalue le résultat 2, que l’on empile La pile contiendra alors dans l’ordre [6*2]. On
considère la parenthèse fermante, on dépile trois élements de sorte à obtenir x=6, op=* et y=2. On
évalue le résultat 12, que l’on empile. On va ensuite empiler + puis 3. Arrivé à la parenthèse fermante,
on dépile trois élements de sorte à obtenir x=12, op=+ et y=3. On évalue le résultat 15, que l’on
empile. On arrive alors à =. On dépile le seul élement de la pile, soit 15 qui est bien le résultat attendu.

2.1. Programmation des primitives

Nous allons travailler avec des piles tabulaires. Avant cela, il faut donc définir les primitives associées.
#define MAX ... /* taille maximale de la pile */
typedef ... ELEMENT /* type des elements dans la pile */
typedef struct {
int sommet; /* indice du sommet de pile */
ELEMENT tab[MAX] ; /* tableau contenant les elements */
} PILE_TAB ;

Sur la base de ce qui vient d’être précisé, nous pouvons définir les différentes primitives réalisant la
gestion des objets définis dans le type :
- test de vacuité (est-ce que la pile est vide)
- test de saturation (est-ce que la pile est saturée)
- initialisation d’une pile à vide
- empiler
- dépiler

Ecrivez un programme permettant de tester ces primitives.


2.2. Évaluation d’une expression bien parenthésées

Dans cette première version, le type ELEMENT correspondra au type char et les éléments de la pile
seront à interpréter soit comme des entiers (pour le cas de chiffres) soit comme des opérateurs. Il vous
est demandé ici de programmer l’évaluation d’expressions arithmétiques selon la méthode décrite
dans l’introduction. Nous ne prendrons pas en compte la divsion.

2.3. Évaluation d’une expression bien parenthésées (avec le type union)

Dans cette seconde version, le type ELEMENT peut être défini en expoitant les types de données de
nature union que nous présentons ci-dessous :

typedef union {
float v; /* pour représenter un réel */
char c; /* pour représenter un caractère */
} EXEMPLE ;

Ce type, identique à struct au niveau syntaxique pour la déclaration, fait coexister dans un
emplacement de la mémoire égal à la plus grande taille des champs (ici float ), une seule
information. Selon que l’on se réfère au champ v, il s’agira d’une valeur de type float , et si l’on se
réfère au champ c, il s’agira d’une valeur de type char. Ainsi, pourront coexister des éléments de
type float ou char. Les types de données de nature union peuvent permettre de gagner un espace
mémoire significatif pour le cas où les champs sont nombreux. Ce ne sera pas le cas ici.

Sachant que dans la pile, il s’agit de faire coexiter soit des nombres, soit des caractères (les
opérateurs), définissez un type ELEMENT adapté à ces circonstances et programmez à nouveau
l’évaluation d’expressions arithmétiques. Dans cet exercice, nous prendrons en compte les divsions et
il sera donc nécessaire de gérer des réels (même si pour les données en entrées, nous ne considérons
encore que les 10 chiffres 0 à 9). Enfin, notez qu’il ne faut pas modifier le code des primitives de
gestion de piles.

3. Exploitation d’une pile tabulaire : dérécursivation

Il est possible de transformer un algorithme récursif en algorithme itératif. Selon la nature de la


récursion, il est possible d’éviter le recours à une pile. C’est le cas du calcul récursif standard de
factoriel. Cela étant, on peut néanmoins calculer factoriel à l’aide d’une pile simulant la récursivité.
Cette approche exploitant de simulation de la récursivité par la gestion d’une pile est plus naturelle
pour l’évaluation du nème nombre de Fibonacci. Dans ce problème, il vous est donc demandé de
fournir une version itérative pour ces deux évaluations (exercices 3.1 pour factoriel et 3.2 pour
Fibonacci).

Vous aimerez peut-être aussi