DEUG MIAS M2


Examen final - 24 avril 1997 - corrigé



[Sujet de l'examen]



Problème (9 points)

On considère des polynômes de degré maximal n de la forme:

P(x) = anxn + an-1xn-1 + ... a1x + a0

1. type abstrait Polynome :

type polynôme
	{ constructeurs }
	polynôme_constant : réel -> polynôme
	construire : polynôme * réel -> polynôme
	{ opérations }
	ième : polynôme * entier -> réel
	éval : polynôme * réel -> réel
 

Le polynôme 3x5 + 2x4 + 5x2 + 3x + 1 est construit comme suit (noter que le terme en x3 a un coefficient de 0) :

construire (
  construire (
    construire (
      construire (
        construire (
          polynôme_constant(3), 2), 0), 5), 3), 1)
 

Les polynômes construits sont successivement :

3 
3x + 2
3x2 + 2x + 0 
3x3 + 2x2 + 0x + 5 
3x4 + 2x3 + 0x2 + 5x + 3 
3x5 + 2x4 + 0x3 + 5x2 + 3x + 1 
 

2. Spécification des fonctions :

Réalisation des fonctions (n est une constante qui définit le degré maximal des polynômes, comme indiqué dans l'énoncé) :

fonction degré (p)
lexique
	d : entier
début
	(* tester les coefficients nuls à partir 
	du terme de plus haut degré *)
	d <- n
	tantque d > 0 et ième (p, n) = 0 faire
		d <- d - 1
	retourner d
 
fonction ajouter (p1, p2)
lexique
	p : polynôme
	i, d : entier
début
	(* construire le polynôme à partir du terme 
	de plus haut degré (voir question 1). 
	Chaque coefficient est la somme des 
	coefficients de même degré de p1 et p2 *)
	d <- max (degré(p1), degré(p2))
	p <- polynôme_constant (ième(p1, d) + ième(p2, d))
	pour i parcourant [d-1 .. 0] faire
		p <- construire (p, ième(p1, i) + ième(p2, i))
	retourner p
 
fonction dérivée (p)
lexique
	dp : polynôme
	i, d : entier
début
	(* la dérivée de anxn + an-1xn-1 + ... a1x + a0
	est n.anxn-1 + (n-1).an-1xn-2 + ... a2x + a1 *)
	d <- degré (p)
	dp <- polynôme_constant (d * ième(p, d))
	pour i parcourant [d-1 .. 1] faire
		dp <- construire (p, i * ième(p, i))
	retourner dp
 
fonction max (p, a, b)
lexique
	m : réel
	x : entier
	y : réel
début
	(* calculer les valeurs de p en a, a+1, ... b
	et conserver la plus grande *)
	m <- éval (p, a)
	pour x parcourant [a+1 .. b] faire
		y <- éval (p, x)
		si y > m alors m <- y
	retourner m
 
 

3. Spécification et réalisation de la fonction Puissance :

Puissance = réel * entier -> réel
fonction Puissance (x, n)	(* n est supposé >= 0 *)
lexique
	res : réel
	i : entier
début
	res <- 1
	pour i parcourant [1 .. n] faire
		res <- res * x
	retourner res
 

Cette fonction nécessite n multiplications et n additions (chaque tour de la boucle pour nécessite d'ajouter 1 à i, d'où les n additions).

Note: on peut calculer cette fonction de façon plus efficace, en constatant que :

Dans les deux cas, xp n'a besoin d'être calculé qu'une fois. S'il faut p multiplications pour calculer xp, pour calculer xn il en faut donc p+1 si n est pair, p+2 si n est impair, avec p = n/2. Bien entendu, xp peut lui-même être calculé selon le même principe. Au total, xn peut être calculé avec environ log(n) multiplications.

4. Réalisation du type abstrait Polynome (n est le degré maximal) :

type polynôme = tableau [0 .. n] de réel;
 

Note : une représentation un peu plus efficace consisterait à utiliser un type produit contenant le tableau des coefficients et le degré du polynôme. Il serait alors intéressant de coder les fonctions de la question 2 par codage implicite.

Réalisation des constructeurs et des opérations du type abstrait :

fonction polynôme_constant (a)
lexique
	p : polynôme
	i : entier
début
	p[0] <- a;		(* terme constant *)
	pour i parcourant [1 .. n] faire
		p[i] <- 0;	(* les autres termes sont nuls *)
	retourner p
 
fonction construire (q, a)
lexique
	p : polynôme
début
	(* le ième coefficient de q devient 
	le i+1ème coefficient de p, et a devient 
	le coefficient constant. Il faut donc que q 
	soit de degré strictement inférieur à n *)
	p[0] <- a
	pour i parcourant [0 .. n-1] faire
		p [i+1] <- q[i]
	retourner p
 
fonction ième (p, i)
début
	retourner p[i]
 
fonction éval (p, x)
lexique
	val : réel
	i : entier
début
	val <- 0
	pour i parcourant [0 .. n] faire
		val <- val + p[i] * Puissance(x, i)
	retourner val
 

5. Nombre de multiplications et d'additions de éval : la boucle pour est parcourue n+1 fois, et dans chaque boucle on a :

Au total, on a donc n+1 + (0 + 1 + 2 + .. + n) additions et autant de multiplications. Sachant que la somme des n premiers entiers est n.(n+1)/2, le nombre d'additions et de multiplications est n+1 + n.(n+1) / 2, soit n2/2+3n/2+1.

Nouvelle réalisation de cette opération en utilisant la forme suivante (dite forme de Horner) : P(x) = ((an.x + an-1).x + an-2).x + ... a1).x + a0

fonction éval2 (p, x)
lexique
	val : réel
	i : entier
début
	val <- p[n]
	pour i parcourant [n-1 .. 0] faire
		val <- val * x + p[i]
	retourner val
 

Dans cette nouvelle version, la boucle est parcourue n fois, et chaque tour de boucle nécessite une multiplication et une addition. Au total, on a donc n additions et n multiplications. Cette version est donc beaucoup plus efficace que celle de la question 4.

Exercice 1 (5 points)

On considère la syntaxe de l'instruction "case" d'un langage proche de Pascal, définie par les règles de production suivantes :

instrcase ::= case <expr> of <listecas> end listecas ::= <cas> | <listecas> ; <cas> cas ::= <listechoix> : <instr> | <> listechoix ::= <choix> | <listechoix>, <choix> choix ::= const | const .. const

1. Diagramme syntaxique équivalent :

2. Dans le langage de réalisation, l'instruction de découpage par cas utilise une expression pour chaque cas. Par exemple :

selon le cas d
	d < 0 : écrire ("pas de solution")
	d = 0 : écrire ("une solution double")
	d > 0 : écrire ("deux solutions")
 

Dans l'instruction case de l'exercice, on compare une même variable à plusieurs constantes. L'exemple ci-dessus ne peut donc pas être traduit par une instruction case. Pour qu'un découpage par cas soit traduisible en une instruction case, il faut que chacune des expression soit de la forme x = constante. ou x >= const1 et
x <= const2
, et que x soit de type entier ou énuméré.

3. Traduire l'instruction case ci-dessous avec des if-then-else :

case carte of
	1 : writeln ('As!');
	2 .. 10 : writeln (carte);
	11, 12, 13 : writeln ('Figure');
end;
 

On obtient une cascade de if-then-else :

if carte = 1
then writeln ('As!')
else	if carte >= 2 and carte <= 10
	then writeln (carte)
	else	if carte = 11 or carte = 12 or carte = 13
		then writeln ('Figure');
 

4. Syntaxe de l'instruction if-then-else sous forme de règles de production :

instrif ::= if <expr> then <instr> <suite> suite ::= else <instr> | <>

5. Résultat de l'exécution des instructions ci-dessous, pour x valant 5, 15 et 50 :

if x < 20 then 
    if x < 10 then writeln ('petit')
else writeln ('pas petit');
 
x = 5	-> petit
x = 15	-> pas petit
x = 50	-> (rien)
 

Note : l'indentation de l'instruction était (intentionnellement) trompeuse : le "else" correspond au second if (if x < 10) et non pas au premier. Si x < 20 est faux, il n'y a pas de partie "else" correspondant, et le programme n'écrit rien.

if x < 20 then 
    if x < 10 then writeln ('petit')
    else writeln ('moyen');
else writeln ('grand');
 
x = 5	-> petit
x = 15	-> moyen
x = 50	-> grand

Exercice 2 (4 points)

On considère le programme suivant :

1	program exam;
2	var i : integer;
3	    a : array[0..4] of integer;
4
5	procedure p1 (var i, j : integer);
6	begin
7	    i := i+1;
8	    j := j+1;
9	    a[i] := a[j-1] +1;
10	end;
11
12	procedure p2 (j : integer);
13	var i : integer;
14	begin
15	    i := 2;
16	    j := j+1;
17	    a[i] := a[j]-1;
18	    a[j] := a[i]+1;
19	end;
20
21	procedure p3 (x : integer);
22	begin 
23	    a[4] := a[4]-1;
24	    x := x+1;
25	    i := 3;
26	    a[i] := a[i]+1;
27	end;
28	
29	begin
30	    a[0] := 0;
31	    a[1] := 1;
32	    a[2] := 2;
33	    a[3] := 3;
34	    a[4] := 4;
35	    i := 0;
36	    p2 (a[2]);
37	    p1 (a[1], a[0]);
38	    p1 (i, i);
39	    p3 (a[4]);
40	    writeln (a[0]+a[1]+a[2]+a[3]+a[4], i);
41	end. 
 

1. Ligne de déclaration correspondant à chaque utilisation de la variable i :

2. Contenu du tableau a et de la variable i (seules sont mentionnées les valeurs affectées) :

                  a[0]   a[1]   a[2]   a[3]   a[4]   i      
après ligne 35    0      1      2      3      4      0      
après ligne 36                  2      3                    
après ligne 37    1      2      2                           
après ligne 38                  3                    2      
après ligne 39                         4      3      3      
 
 

L'affichage final du programme est donc 13, 3.

Question de cours (2 points)

(voir cours).

 


Michel Beaudouin-Lafon, mbl@lri.fr