Correction du DS n1 Python
Transcription
Correction du DS n1 Python
PSI 2014/2015. Correction DS n◦ 1 Les appareils électroniques sont interdits. Exercice 1 (Connaissance du python) 1. Soit une liste vals = [ 1, 5, -3, 8, 9, -4, -2, 7 ] Écrire une boucle qui remplace, directement dans la liste vals, toutes les valeurs négatives par leur carré. Après l’exécution de la boucle, la liste vals devrait donc contenir : [ 1, 5, 9, 8, 9, 16, 4, 7 ] for i in range ( len ( vals )): if vals [ i ] < 0: vals [ i ] = vals [ i ] ** 2 2. Soit une liste lst définie de la façon suivante : lst = [(1,3),(5,'a'), 42, "x", 1+1 == 1, 9+1]. Que valent les expressions suivantes ? lst[2] : 42 lst[1][1] : 'a' lst[1:4] : [ (5,'a'), 42, 'x'] lst[-2] : False lst[3:] : ['x', False, 10] lst[:] : [(1,3),(5,'a'), 42, 'x', False, 10] lst[::-1] : [10, False, 'x', (5,'a'), (1,3) ] 3. Valeurs entières produites par l’instruction range(3,11,2) : 3,5,7,9 4. Soit une variable age contenant une valeur numérique entière. Donner deux exemples différents d’utilisation d’un appel à la fonction print() afin d’afficher la valeur de la variable age suivant l’exemple suivant : avec age = 23 on veut l’affichage Vous avez 23 ans. • print('Vous avez', age, 'ans') • print('Vous avez ' + str(age) + ' ans') • print('Vous avez', end=' ') print(age, end=' ') print('ans ') • print('Vous avez {0} ans'.format(age)) Exercice 2 (Algorithme d’exponentiation rapide ) Pour calculer k n , on se propose d’exploiter les relations suivantes : 2p k = (k 2 )p k 2p+1 = k × (k 2 )p 1. Utiliser ces relations pour donner une version récursive du calcul de k n . 1 def exponenti at i o n_ r ap i d e (x , n ): if n == 0: return 1 else : if n % 2 == 0: return exponenti a ti o n_ r a pi d e ( x * x , n //2) else : return x * exponentia t io n _r a p id e ( x * x , n //2) 2. Établir la terminaison effective de cet algorithme. Une simple récurrence (forte) sur n... 3. (a) Quel est la complexité opératoire (ie en nombre d’opérations arithmétiques) de cet algorithme ? La comparer avec la version « naïve » du calcul de k n . On note T (n) le nombre de multiplication totale effectuées pour la puissance n. A chaque appel récursif (sauf 0), on fait une ou deux multiplications. On a donc j n k j n k T (0) = 0 et ∀ n ∈ N∗ , T + 1 6 T (n) 6 T +2 2 2 jnk < 2d . (puisque 2d−1 6 n2 + 21 < 2d .) Soit d tel que 2d 6 n < 2d+1 . On a alors 2d−1 6 2 On fera donc d + 1 appels récursifs avant d’appeler T (0). Donc au mieux d + 1 multiplication, au pire 2 ∗ (d + 1) multiplications. La complexité est en O ln(n) , alors que la version « naîve » effectue n − 1 = O(n) multiplications (b) Donner un exemple d’entiers entre 9 et 15 où la complexité opératoire est la plus haute. D’après la question précédente, le pire est obtenue pour n = 15 (on est alors dans le cas d = 3). On a T (15) = 2 + T (7) = 4 + T (3) = 6 + T (1) = 8 + T (0) = 8. Exercice 3 (Débogage) export = 70.0 1. import = 87.7 print ( " Balance commerciale 2011 avec l ’ Allemagne : " , export - import ) Quel est la cause de l’erreur de syntaxe de ce programme ? : En Python import est un mot clé réservé. On ne peut pas l’utiliser comme identificateur de variable. 2. On écrit le programme suivant et on l’exécute : import math def dist ( p_x , p_y ): """ Distance de (0 ,0) à (x , y ) """ return math . sqrt ( p_x ** 2 + p_y ** 2) Or il ne se passe rien. Pourquoi ? Que doit on rajouter au programme pour qu’il se passe quelque chose ? On se contente de définir une fonction et on exécute aucun code d’appel à cette fonction. Et pour voir quelque chose, il faudrait après la fonction taper print(dist(p_x, p_y)) 3. Pour afficher les carrés des nombres de un à cent, en excluant les multiples de 5, on a écrit le programme suivant : 2 maxi = 100 i = 1 while ( i <= maxi ) and ( i %5 != 0): print (i , i **2) i = i +1 Or, le programme n’affiche que les quatre premiers carrés. Pourquoi ? Comment corriger ce programme ? Parce qu’arrivé à i=5, la condition de boucle devient fausse et la boucle s’arrête simplement. Pour corrigé ce programme, il faut changer la boucle : while i <= maxi : if i % 5 != 0: print (i , i **2) i += 1 4. On a écrit une fonction qui, en lui donnant la longueur des cotés, calcule le périmètre et l’aire d’un triangle et les renvoi. from math import sqrt def perimairet ri an g le ( c1 , c2 , c3 ): " Calcul de p é rim è tre et aire d ’ un triangle " p = c1 + c2 + c3 # Calcul de surface par la formule de H é ron ps2 = p /2 a = sqrt ( ps2 *( ps2 - c1 )*( ps2 - c2 )*( ps2 - c3 )) print (p , a ) Or chaque fois qu’on utilise cette fonction dans un programme, elle retourne None. Pourquoi ? Comment rectifier ceci ? Il n’y a aucune instruction de retour de valeur dans la fonction, donc elle retourne None (même si elle a affiché les résultats via le print). Pour corriger ceci, remplacer le print par un return. Exercice 4 (Autour des piles) 1. Question de cours : Écrire les fonctions creer_pile(), pile_est_vide(pile), empiler(pile,x) et depiler(p) permettant d’implémenter des piles à capacité illimitées à l’aide d’une liste. def creer_pile (): return [] def empiler (p , x ): p . append ( x ) def pile_est_vide ( p ): return p == []: def depiler ( p ): return p . pop () 2. Écrire une fonction echange_pile(pile) prenant en paramètre une pile contenant au moins deux éléments, et qui à l’aide des seules fonctions de la question précédente, échange dans la pile son premier élément avec son dernier élément. def echange_pile ( pile ): p = creer_pile () debut = depile ( pile ) while not pile_est_vide ( pile ): empile (p , depile ( pile )) fin = depile ( p ) empile ( pile . debut ) while not pile_est_vide ( p ): empile ( pile , depile ( p )) empile ( pile . fin ) 3 3. Une application des piles : la notation polonaise inversée On appelle notation polonaise inversée ou expression algébrique postfixée une écriture suivant laquelle les opérateurs arithmétiques usuels (+, −, ×, :) sont placés après les chiffres et non pas entre. Ainsi 1 + 2 se noterait 1 2 + et 1 + 2 × 3 se noterait 1 2 3 × +. L’intérêt de cette notation est que les parenthèses deviennent superflues. Par exemple l’opération (1 + 2) × 3 se noterait 1 2 + 3 ×, l’opération (10 + 3) × (15 − 7) se noterait 10 3 + 15 7 - * . Écrire une fonction evalue_npi(pile) qui évalue une notation polonaise inversée. L’opération sera passé sous forme d’une liste. Par exemple, pour obtenir l’évaluation de 10 3 + 15 7 - *, on tapera evalue_npi([10,3,'+',15,7,'-','*']). On supposera que l’opération fournie est syntaxiquement valide. L’idée est simple, on avance dans la liste : • Si c’est un chiffre, on l’empile dans une liste/pile nbres. • Si c’est un symbole d’opération, on récupère les deux derniers nombres dans nbres en depilant, on effectue l’opération, et on empile le resultat dans nbres • À la fin, nbres ne contient qu’un élément à savoir le résultat de l’opération def evalue_pile ( lst ): nbres =[] for i in lst : if i not in ['*','+','-',':','/'] # ou if isinstance (i , int ): nbres . append ( i ) else : a = nbres . pop () b = nbres . pop () if i == '*': nbres . append ( a * b ) elif i == '+': nbres . append ( a + b ) elif i == '-': nbres . append ( b - a ) else i == ':' or i == '/': nbres . append ( b / a ) return nbres [0] Exercice 5 On appelle palindrome un mot qui peut se lire indifféremment de gauche à droite ou de droite à gauche. Ainsi par exemple : radar, rotor ou kayak sont trois palindromes. 1. Écrire une fonction palindrome() prenant en paramètre une chaîne de caractère et qui retourne True si il s’agit d’un palindrome et False sinon. Les espaces éventuels seront gérés comme de simple caractères def palindrome ( s ): return s [:: -1] == s ou un peu plus long def palindrome ( s ): s_inverse = '' for i in s : s_inverse = i + s_inverse return s == s_inverse 2. Améliorer la fonction palindrome() pourqu’on ne différencie pas les majuscules des minuscules. Pour cela on pourra utiliser la méthode de liste suivante : lower(...) | S.lower() -> str | | Return a copy of the string S converted to lowercase. 4 def palindrome ( s ): return s [:: -1]. lower () == s . lower () 3. Écrire de même une fonction récursive palindrome_rec() qui fait la même chose. Pour cela on remarquera que : • Tout mot de longueur inférieur à 1 est un palindrome. • Un mot est un palindrome si et seulement si ses premier et dernier caractères sont identiques et son sous-mot allant du 2ème caractère jusqu’à l’avant-dernier est un palindrome. def palindrome_r ec ( chaine ): if len ( chaine ) <= 1: return True if chaine [0] == chaine [ -1]: return True + palindrome_r e c ( chaine [1: -1]) return False 4. Quelle est la complexité de la fonction palindrome_rec() dans le pire des cas ? (en fonction de la longueur du mot.) j k n Pour un mot de longueur n il y aura appels récursifs, et à chaque appel l’extraction dans un 2 mot de longueur n du sous-mot allant du 2ème à l’avant-dernier caractère prendra de l’ordre de n − 2 opérations auquel on rajoute deux comparaisons La complexité dans le pire des cas vérifie T (n) = n + T (n − 2), T (0) = T (1) = 1. Soit O(n2 ) comme somme des premiers termes d’une suite arithmétique. Exercice 6 Pour implémenter une matrice en python on utilisera une liste de listes numériques (les lignes), toutes ayant même longueur. Ainsi, par exemple : 1 2 3 M = [[1,2,3], [4,5,6]] implémente la matrice carrée M = 4 5 6 1. Quel code renvoie le coefficient de M à la ligne i et colonne j ? : M[i-1][j-1] 2. Si M ∈ Mn,p (K), que renvoie l’instruction python len(M) ? : n 3. Que fait la fonction ci dessous (M étant une matrice, a et b deux entiers) ? Quelle est sa complexité ? def f (M ,a , b ): La fonction f retourne la sous-matrice de M obtenue n = len ( M ) en supprimant sa ligne d’indice a (ligne a + 1) et de R = [ ] sa colonne d’indice b (colonne b + 1). for i in range ( n ): Sa complexité est quadratique (deux boucles for imif i != a : briquées, chacune se déroulant n fois). L = [ ] for j in range ( n ): if j != b : L . append ( M [ i ][ j ]) R . append ( L ) return R 4. Écrire une fonction récursive prenant en paramètre une matrice carrée et qui retourne son detérminant. On utilisera comme condition terminale que le déterminant de la matrice (m) ∈ M1,1 (K) est m. n X (−1)i+j mi,j ∆i,j Pour rappel det(M ) = i=1 On va caluler det(M ) en développant par rapport à la première ligne : 5 def det ( M ): if len ( M ) == 1: return M [0][0] S = 0 for j in range ( len ( M )): S += ( -1)**(1+ j )* M [0][ j ]* det ( f (M ,0 , j )) return S 6