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