Lycée Alphonse Daudet MPSI Année 2015-2016 Ds Informatique DS 02 d'informatique (devoir sur table) Durée : 2h30. Corrigé. Exercice 1. Représentations des nombres. Vous pouvez vous aider de la calculcatrice pour les calculs, mais ceux-ci doivent être détaillés sur votre copie. 2 1. Écrire l'entier six cent douze en base 2. Écrire en base 10 l'entier dont l'écriture en base 2 est 101001101 . • On eectue les divisions euclidiennes par 2 successives : 612 = 2 × 306 + 0 306 = 2 × 153 + 0 153 = 2 × 76 + 1 76 = 2 × 38 + 0 38 = 2 × 19 + 0 19 = 2 × 9 + 1 9=2×4+1 4=2×2+0 2=2×1+0 1=2×0+1 2 D'où 612 = 1001100100 . 2 • 101001101 = 1 + 0 × 2 + 1 × 4 + 1 × 8 + 0 × 16 + 0 × 32 + 1 × 64 + 0 × 128 + 1 × 256 = 333 . 2. Donner le codage sur 16 bits de l'entier relatif −1234 (par la méthode du complément à 2). On commence par coder 1234 comme dans la question précédente. On trouve 0000010011010010. Puis on calcule 1111111111111111−0000010011010010 = 1111101100101101. Enn, on ajoute 1. On trouve 1111101100101 3. On imagine que les ottants sont codés sur 16 bits, le premier bit étant le bit de signe, les 6 suivants les bits de l'exposant et ce qui reste étant destiné à la mantisse. Donner le codage de 0.42. Ce codage est-il dèle ? • Le nombre est positif donc le bit de signe est 0. Pour trouver l'exposant on multiplie par 2 jusqu'à obtenir un nombre supérieur à 1 : 0.42 × 2 = 0.84 < 1. 0.84 × 2 = 1.68 > 1. 2 Donc l'exposant est −2 = 111110 . On détermine la mantisse : 0.68 × 2 = 1.36. 0.36 × 2 = 0.72. 0.72 × 2 = 1.44. 0.44 × 2 = 0.88. 0.88 × 2 = 1.76. 0.76 × 2 = 1.52. 0.52 × 2 = 1.04. 0.04 × 2 = 0.08. 0.08 × 2 = 0.16. Lycée Alphonse Daudet MPSI Année 2015-2016 Ds Informatique Et on s'arrête car on a nos 9 bits (mais on a dû tronquer, on n'est pas arrivé à 1.00). Finalement, le codage est 0111110101011100 . Remarque : dans la norme IEE754 et contrairement à ce qu'on a écrit ici, l'exposant n'est pas exactement codé comme un entier relatif. • On a dû tronquer la mantisse, ce codage n'est donc pas dèle (un autre argument est que 0.42 a pour écriture fractionnaire irréductible 21 50 qui n'est pas de la forme M 2n ). Exercice 2. Entrées / sorties. 4. Que se passe-t-il lorsqu'on exécute le code suivant ? Détaillez autant que possible votre réponse. a d r e s s e = in pu t (" Q u e l l e a d r e s s e ?") F i c h i e r L e c t u r e = open ( a d r e s s e , ' r ' ) TexteLu = F i c h i e r L e c t u r e . read ( ) FichierLecture . close () p r i n t ( TexteLu ) i f "MPSI" not i n TexteLu : hacker = in pu t ("Nom de g u e r r e ?") F i c h i e r E c r i t u r e = open ( a d r e s s e , ' w' ) F i c h i e r E c r i t u r e . w r i t e (" Ce f i c h i e r n ' a v a i t aucun i n t é r ê t . \ nSigné "+hacker ) FichierEcriture . close () i f l e n ( TexteLu ) >100000: p r i n t ("Un v r a i carnage . " ) • Un message s'ache sur la console en demandant à l'utilisateur de saisir l'adresse d'un chier. Le chier est lu, puis refermé, le contenu du chier s'ache dans la console. Si le chier ne contenait pas la chaîne de caractère "MPSI", un message s'ache sur la console en demandant à l'utilisateur de saisir un nom ; le contenu du chier est eacé et remplacé par les deux lignés "Ce chier n'avait aucun intérêt." et "Signé " suivi du nom donné. Si de plus le chier faisait plus de 100000 caractères, le message "Un vrai carnage." est aché sur la console. • Après l'exécution, la variable adresse contient l'adresse du chier, la variable hacker contient le nom renseigné, la variable TexteLu contient le contenu du chier (ces trois variables sont de type chaîne de caractères). Exercice 3. Suite de Fibonacci. (D'après une annale de concours en fait CCP MP 2016.) On note (Fn )n∈N la suite des nombres de Fibonacci dénie par : F0 = 0, F1 = 1, ∀n ∈ N, Fn+2 = Fn+1 + Fn . 5. Écrire une fonction Fib qui prend en argument un entier naturel n et retourne le nombre de Fibonacci Fn . Par exemple, Fib(6) retournera 8. Par exemple : def Fib(n): (u,v)=(0,1) for i in range(0,n): (u,v)=(v,u+v) return(u) Lycée Alphonse Daudet MPSI Année 2015-2016 Ds Informatique 6. Déterminer le nombre d'additions eectuées lors d'un appel de votre fonction. Vous pourrez (sans obligation) écrire la réponse sous la forme O(f (n)) où f est une fonction simple. Il y en a une dans la boucle (et seulement dans la boucle), donc une par tour de boucle, donc n, donc O(n). Bien sûr, cela dépend de ce qu'on a proposé comme fonction. Exercice 4. PGCD. (D'après une annale de concours en fait CCP MP 2016 aussi.) 7. Pour calculer le pgcd de deux entiers, on peut utiliser l'algorithme d'Euclide. Écrire une fonction Python Euclide(a,b) qui prend comme argument deux entiers a et b qu'on supposera positifs et donne comme résultat leur pgcd calculé à l'aide de l'algorithme d'Euclide. Cf cours. 8. On propose dans cette question une autre stratégie pour calculer le pgcd de deux entiers a et b, mais seulement en les supposant strictement positifs. On l'illustre à l'aide d'un exemple : pour calculer le pgcd de 3705 et 513, on peut passer en revue tous les entiers 1, 2, 3, · · · , 512, 513 puis renvoyer parmi ces entiers le dernier qui divise à la fois 3705 et 513. Il sera alors bien le plus grand des diviseurs commun à 3705 et 513. Écrire une fonction gcd qui renvoie le pgcd de deux entiers naturels strictement positifs, selon la méthode décrite ci-dessus. On pourra éventuellement utiliser librement l'instruction min(a,b) qui calcule le minimum de a et b. Par exemple gcd(3705 ,513) renverra 57. La réponse attendue ressemble certainement à : def gcd(a,b): n=min(a,b) d=1 for k in range(2,n): if a%k==0 and b%k==0: d=k return(d) 9. Laquelle des deux fonctions Euclide et gcd vous paraît meilleure pour calculer le strictements positifs ? Argumentez. pgcd de deux entiers Avec gcd on calcule 2min(a,b)-4 restes de divisions euclidiennes. Avec l'algorithme d'Euclide on en calcule O(lg(min(a,b))) (même si on ne sait pas qu'on en calcule O(lg(min(a,b))), il est clair qu'on en calcule beaucoup moins que min(a,b) puisqu'après chaque calcul de division euclidienne, on recommence sur le reste, qui est en général strictement plus petit que le dividende moins un !). Bref, Euclide est bien meilleur. Exercice 5. Exponentiation modulaire d e f p u i s ( x , n ,N) : # x , n , N des n a t u r e l s avec N>0 r e s u l t a t =1 compteur=n w h i l e compteur ! = 0 : r e s u l t a t =( r e s u l t a t ∗ x)%N compteur=compteur −1 return ( r esu lt at ) 10. Montrer que cette fonction termine. La fonction a une seule boucle et n'utilise pas la récursivité. Il sut de montrer que la boucle while termine. Pour cela, montrons que compteur est un variant de boucle. Notons compteurk l'état de cette variable à l'issue de k tours de boucle. On a compteurk+1 = compteurk − 1 donc (compteurk )k est bien une suite Lycée Alphonse Daudet MPSI Année 2015-2016 Ds Informatique strictement décroissante d'entiers naturels (puisque compteur0 = n est un entier naturel par hypothèse) i. e. compteur est bien un variant de boucle. D'où la terminaison. 11. Donner un invariant de boucle pertinent pour la boucle while et en déduire que cette fonction permet de calculer le reste dans la division euclidienne de xn par N . Montrons que (resultat*x**compteur)% N est un invariant de boucle. En notant resultatk l'état de la variable resultat après k tours de boucle, on a : (resultatk+1 ∗xcompteurk+1 )%N = (((resultatk ∗x)%N )∗xcompteurk −1 )%N = (resultatk ∗x∗xcompteurk −1 )%N par comptatibilié du modulo avec les produits. Autrement dit (resultat*x**compteur)% N est bien un invariant de boucle. D'où on tire resultatn = resultatn ∗ x0 %N = resultatn ∗ xcompteurn %N = resultat0 ∗ xcompteur0 %N = 1 ∗ xn %N = xn %N , cqfd. 12. Quel pourrait être l'avantage de cette fonction par rapport à l'instruction (x**n)%N ? La fonction précédente eectue n produits d'entiers compris entre 0 et N − 1. Or les produits d'entiers coûtent d'autant plus chers que les entiers qu'on multiplie sont grands. Si N est petit, mais que x ou surtout n est très grand, l'entier x**n est gigantesque et le calcul de x**n probablement bien plus coûteux que les n petits produits de la fonction précédente. Exercice 6. Un tri. (D'après une annale de concours en fait Mines épreuve commune 2016.) On se donne la fonction Tri suivante, écrite en Python : d e f Tri (L ) : # L e s t une l i s t e non v i d e n=l e n (L) f o r i i n range ( 1 , n ) : j=i x=L [ i ] w h i l e 0< j and x<L [ j − 1 ] : L [ j ]=L [ j − 1] j=j −1 L [ j ]=x 13. Lors de l'appel Tri(L) lorsque L est la liste [5, 2, 3, 1, 4], donner le contenu de la liste L à la n de chaque itération de la boucle for. On obtient successivement n=5 i=1x=2 i=2x=3 i=3x=1 i=4x=4 [2, 5, 3, 1, 4] [2, 3, 5, 1, 4] [1, 2, 3, 5, 4] [1, 2, 3, 4, 5] 14. Donner un variant de boucle pour la boucle while. En déduire rapidement que la fonction Tri termine toujours. La variable j est un variant, même argument que dans l'exercice précédent. Ainsi la boucle while termine toujours, quant à la boucle for elle se termine à l'issue de n − 1 itérations. 15. Soit L une liste non vide d'entiers ou de ottants. On rappelle que L[0:i+1] désigne la liste [L[0],L[1],...,L[i]]. Montrer que L[0:i+1] est triée par ordre croissant et {L[0], . . . , L[n − 1]} sont des invariants de boucle. En déduire que tri(L) trie la liste L. Lycée Alphonse Daudet MPSI Année 2015-2016 Ds Informatique Notons P (k) : à l'issue de l'itération pour la valeur i = k, la valeur Lk de la variable L est telle que Lk [0 : k + 1] est la liste initiale L0 [0 : k + 1] triée dans l'ordre croissant, et Lk [k + 1 :] = L0 [k + 1 :]. • La liste L0 [0 : 0 + 1] est la liste ayant une seule valeur L0 [0] donc P (0) est vrai (à l'issue de l'itération pour i = 0 signiant avant d'entrer dans l'itération pour i = 1 i.e. la première). • Supposons P (k) vrai et k + 1 6 n (i.e. on fait encore une itération). Comme j prend initialement la valeur k + 1 et ne peut que décroître, et comme on ne modie que la valeur de L[j], on aura bien Lk+1 [k + 2 :] = Lk [k + 2 :] = L0 [k + 2 :] (via P (k)). On a aussi Lk [0 : k + 1] = [x0 , . . . xk ] avec x0 6 · · · 6 xk . Au début de l'itération j vaut k + 1 et x = Lk [k + 1] = L0 [k + 1]. La boucle "while" permet de faire décroître j et de s'arrêter dès que Lk [j] 6 x. Quand on s'arrête, on a donc dans L[0 : k + 1] la valeur [x0 , · · · xj , xj , · · · xk ], puis la ligne 9 assure Lk+1 [0 : k + 1] = [x0 , · · · xj−1 , x, xj , · · · xk ] i.e. c'est bien Lk [0 : k] + [x] = Lk [0 : k + 1] trié par ordre croissant et via P (k), c'est bien aussi L0 [0 : k + 1] trié par ordre croissant. • Ainsi P (k) est bien un invariant de boucle. Ce qui implique que les deux expressions de l'énoncé en sont aussi. 16. Évaluer le nombre de comparaisons réalisées lors de l'appel de Tri(L) dans le pire des cas, en fonction de n=len(L). Vous pourrez (sans obligation) écrire la réponse sous la forme O(f (n)) où f est une fonction simple. Les seules comparaisons sont dans le test de la boucle while (qui réalise deux comparaisons). Dans le pire des cas la boucle while s'exécute i − 1 fois : on a donc au pire 2i comparaisons (en comptant celles après lesquelles la boucle ne s'exécute plus). Ceci a lieu pour tous les i allant de 1 à n − 1, ce qui donne donc au total n−1 X i=1 2i = 2 n(n − 1) = O(n2 ) comparaisons. 2