Correction du devoir surveillé 1 - Informatique
Partie I : Lecture de programmes Exercice 1 — Un air de déjà vu
Considérons de la fonction suivante.
def mystere(q, n):
S = 0 a = 1
for k in range(n):
S += a * q ** k a = -a
return S
La fonction mystere prend en arguments un nombre q (qui peut être entier, flottant ou même complexe) et un entier n, et renvoie la valeur de la somme
n−1
X
k=0
(−1)kqk.
Justifions-le en décrivant le fonctionnement de la fonction.
• Tout d’abord, on initialise une variable S à 0et une variable a à1.
• On rentre ensuite dans la boucle for for k in range(n):
dans laquelle la variable k parcourt tous les entiers de 0à n−1. Soit k ∈J0;n−1K.
Alors au tour k de la boucle :
? on ajoute à la variable S le terme aqk;
? on multiplie ensuite la variable a par −1.
Ainsi, la variable a vaut alternativement1 ou−1 : comme elle est initialisée à 1, la variable a vaudra (−1)k au début du tourk de la boucle .
Donc au tourkde la boucle, le terme que l’on ajoute à la variable S est(−1)kqk.
• À la fin de la boucle, la fonction renvoie la valeur de S, c’est-à-dire la somme pour k allant de 0à n−1 des (−1)kqk : la fonction renvoie
n−1
X
k=0
(−1)kqk .
Exercice 2 — Affichages mystère 1. Le script suivant
n = 0
while n < 15:
n = n + 2 print(n) affiche 16. 2. Le script suivant
n = 10
while n >= 11:
n = n + 2 print(n) affiche 10. 3. Le script suivant
n = 0
for k in range(5):
n = n + 1 print(n) affiche 5.
4. Le script suivant n = 0
for k in range(5):
n = n + 1 print(k)
affiche 4, la dernière valeur prise par la variable k dans la boucle.
5. Le script suivant
def fonction(a):
a += 2.0 return a a = fonction(8.0) print(a)
affiche 10.0.
6. Le script suivant a = 7 b = 12 if a > 5:
b = b - 4 if b >= 10:
b = b + 1 print(b) affiche 8.
7. Le script suivant a = 10 if a < 5:
a = 20 elif a < 100:
a = 500 elif a < 1000:
a = 1 else:
a = 0 print(a) affiche 500. 8. Le script suivant
for i in range(1, 10, 3):
print(i * i, "i * i") produit les affichages suivants :
1 i∗i 16 i∗i 49 i∗i
.
Partie II : Écriture de programmes
Problème 1 — Jeu du beuzeu
1. La fonction suivante prend en argument un entier n et renvoie True si l’entier n est un multiple de 7, False sinon.
def multiple_sept(n):
"""
Entrée : un entier n.
Sortie : True si n est un multiple de 7, False sinon.
"""
return (n % 7 == 0)
# On retourne un booléen.
2. (a) Pour déterminer si un caractère est présent dans une chaine de caractères donnée, on parcourt un à un les caractères de la chaine. Si le caractère rencontré est le caractère cherché, on renvoie Trueet on sort de la fonction.
Si à la fin du parcours, on n’est pas sorti de la fonction, c’est que le caractère n’est pas présent dans la chaine, et on renvoie alors False.
def contient(caractere , chaine):
"""
Entrées : un caractère caractere ,
une chaine de caractères chaine.
Sortie : True si la chaine contient le caractère , False sinon.
"""
for c in chaine:
if c == caractere:
return True return False
(b) Pour déterminer si l’écriture décimale d’un entier contient le chiffre 7, on commence par convertir l’entier en chaine de caractères à l’aide de la com- mande str. On applique alors la fonction de la question précédente à cette chaine de caractères et au caractère"7".
def contient_sept(n):
"""
Entrée : un entier n.
Sortie : True si l'écriture décimale de n contient le chiffre 7,
False sinon.
"""
chaine = str(n)
# On considère la chaine constituée des chiffres de n.
return contient("7", chaine)
3. Les entiers naturels interdits au jeu du beuzeu sont les entiers qui sont multiples de7ou dont l’écriture décimale contient le chiffre 7. Pour déterminer si un entier est un coup autorisé au jeu du beuzeu, on vérifie donc qu’il n’est pas interdit.
def coup_autorise(n):
"""
Entrée : un entier naturel n.
Sortie : True si l'entier n est autorisé au beuzeu , False sinon.
"""
return not(multiple_sept(n) or contient_sept(n))
4. La fonction suivante prend en argument un entier N et renvoie la liste des N premiers coups autorisés au jeu du beuzeu.
• Pour ce faire, elle commence par initialiser la liste des coups à la liste vide.
Elle crée aussi une variable nombre_coups, initialisée à 0, qui compte les coups déjà joués.
• On part ensuite d’un entier nqui vaut 1. Tant que le nombre total de coups joués n’a pas atteint N, la fonction fait la chose suivante.
? Si l’entiernest autorisé au jeu du beuzeu, on l’ajoute à la liste des coups autorisés. On incrémente alors de 1le compteur de coups joués.
On passe ensuite à l’entier suivant.
? Sinon, on passe directement à l’entier suivant.
• Au sortir de la boucle while, la liste des coups autorisés contient lesN coups joués. On renvoie alors cette liste.
def beuzeu(N):
"""
Entrée : un entier N.
Sortie : la liste des N premiers coups autorisés au beuzeu.
"""
Liste = []
nombre_coups = 0 n = 1
while nombre_coups < N:
if coup_autorise(n):
Liste.append(n) nombre_coups += 1 n = n + 1
return Liste
Problème 2 — The look-and-say sequence 1. • Avec une boucle while :
? Pour compter le nombre de fois où le premier élément d’une liste appa- rait en début de liste, on commence par créer une variable compteur, initialisée à 1.
? On crée alors une variable k, initialisée à l’indice 1, qui parcourra les indices des éléments de la liste L, en commençant par le deuxième élé- ment (on a initialisé le compteur à 1, donc on a déjà compté une fois le premier élément de la liste).
Tant que l’indicekne dépasse pas la longueur de la liste (c’est-à-dire tant quek est l’indice d’un élément de la liste) et que l’élément d’indicek est égal au premier élément de la liste, on ajoute 1à la variablecompteur : on a trouvé une occurrence de plus du premier élément, et on passe à l’indice suivant.
? On sort de la boucle soit lorsque tous les éléments de la liste ont été regardés, soit lorsque l’on rencontre pour la première fois un élément différent du premier. On renvoie alors la valeur de la variable compteur.
def Segment_initial(L):
"""
Entrée : une liste L.
Sortie : le nombre d'occurrences consécutives
du premier élément au début de la liste L.
"""
premier = L[0] # Le premier élément de la liste.
compteur = 1 k = 1
while k < len(L) and L[k] == premier:
compteur += 1 k = k + 1 return compteur
• Avec une boucle for :
On peut également écrire cette fonction à l’aide d’une boucle for. Pour ce faire, on procède de la manière suivante.
? On crée une variable compteur, initialisée à 0, qui comptera encore le nombre d’occurrences du premier élément au début de la liste.
Cette fois-ci, comme le parcours de la liste se fera sur les éléments et non sur les indices, le parcours ne commencera pas au deuxième élément mais au premier. Ainsi, on ne compte pas le premier élément une fois avant le parcours : on initialise le compteur à 0 et non à 1.
? On parcourt ensuite un à un les éléments de la liste.
Si l’élément rencontré est égal au premier de la liste, on ajoute 1 à la variable compteur.
Par contre, si l’on rencontre un élément différent du premier, on renvoie la valeur du compteur, ce qui arrête la fonction.
? À la fin du parcours, si l’on n’est pas sorti de la fonction, c’est que tous les éléments de la liste étaient en fait égaux au premier. On renvoie alors le compteur, qui a pour valeur la longueur de la liste.
def Segment_initial(L):
"""
Entrée : une liste L.
Sortie : le nombre d'occurrences consécutives
du premier élément au début de la liste L.
"""
premier = L[0] # Le premier élément de la liste.
compteur = 0 for element in L:
if element == premier:
compteur += 1 else:
return compteur return compteur
2. La fonction Look_and_say suivante prend en entrée une liste L représentant un terme de la suite et renvoie le terme suivant dans la suite logique.
def Look_and_say(L):
"""
Entrée : une liste L, représentant un terme de la suite.
Sortie : la liste représentant le terme suivant dans la suite.
"""
Resultat = []
while len(L) != 0:
nombre = Segment_initial(L)
Resultat = Resultat + [nombre , L[0]]
del(L[0 : nombre])
# On supprime tous les chiffres déjà comptés ,
# c'est-à-dire les nombre premiers éléments de L.
return Resultat
On construit le terme suivant dans la suite logique au fur et à mesure, en partant de la liste vide, de la manière suivante.
• À l’aide de la fonction précédente, on trouve le nombre d’occurrences du premier élément deL en début de liste, que l’on appelle nombre. On ajoute alors à notre résultat ce nombre d’occurrences suivi de la valeur du premier élément de L.
• Une fois ce premier élément compté, on supprime lesnombreoccurrences du premier élément de la liste, c’est-à-dire tout le début de la liste.
• Puis on recommence avec les éléments restants de la listeL, jusqu’à ce que tous ses éléments aient été comptés, c’est-à-dire que la listeL soit vide.
3. La fonction Suite_logique suivante prend en entrée un entier n et renvoie le n-ième terme de la suite logique.
Pour ce faire, elle part du premier terme, u0, de la suite, puis elle applique n−1 la fonction Look_and_say, c’est-à-dire qu’elle calcule n−1 nouveaux termes de la suite. À l’issue de ces n−1calculs, le terme obtenu est le terme un−1, qui est bien le n-ième terme de la suite.
def Suite_logique(n):
"""
Entrée : un entier n.
Sortie : le n-ième terme u_{n - 1} de la suite logique.
"""
terme = [1] # Cette liste représente le terme u_0.
for k in range(n - 1):
# On passe n - 1 fois au terme suivant
# pour arriver au n-ième terme.
terme = Look_and_say(terme)
# On passe du terme u_k au terme u_{k + 1}.
return terme
Problème 3 — Phylogénie
1. La fonctionAjout_especesuivante prend en entrée la liste des données phylogé- nétiques ainsi qu’une nouvelle espèce, et ajoute à la liste des données la nouvelle espèce.
def Ajout_espece(Donnees_phylo , espece):
"""
Entrées : la liste Donnees_phylo des données phylogénétiques , une espèce espèce.
Ajoute l'espèce à la liste des données.
Sortie : aucune , la liste Donnees_phylo est directement modifiée.
"""
Donnees_phylo.append(espece)
2. La fonction Ajout_caracteresuivante prend en entrée la liste des données phy- logénétiques ainsi qu’une liste d’états (0 ou 1), et ajoute à chaque espèce un nouveau caractère, dont l’état est déterminé par la liste d’états.
def Ajout_caractere(Donnnees_phylo , Etats):
"""
Entrées : la liste Donnees_phylo des données phylogénétiques , une liste Etats d'états.
Ajoute à chaque espèce de la liste Donnees_phylo
l'état à la même position dans la liste d'états.
Sortie : aucune , la liste Donnees_phylo est directement modifiée.
"""
if len(Donnees_phylo) != len(Etats):
print("Erreur ! Il n'y a pas autant d'états du nouveau caractère que d'espèces.")
else:
for k in len(Etats):
Donnees_phylo[k].append(Etats[k])
• S’il n’y a pas autant d’états listés que d’espèces (c’est-à-dire si les listes données en entrée n’ont pas la même longueur), on affiche un message d’er- reur : il n’est pas possible de faire correspondre chaque état à une espèce et inversement.
• Sinon, une variablekparcourt les indices des deux listes. Pour chaque indice k, on ajoute à l’espèce d’indice k dans la liste des données phylogénétiques l’état se trouvant à l’indice k dans la liste des états.
3. (a) La fonction Est_ancetre suivante prend en entrée deux espèces et renvoie True si la première espèce est ancêtre de la seconde, et False sinon.
def Est_ancetre(espece1 , espece2):
"""
Entrées : deux espèces espece1 et espece2.
Sortie : True si espece1 est ancêtre de espece2 , False sinon.
"""
if len(espece1) != len(espece2):
print("Erreur ! Ces deux espèces n'ont pas le même nombre de caractères.")
else:
for k in len(espece1):
if espece2[k] == 0 and espece1[k] == 1:
return False return True
• Si les deux espèces n’ont pas le même nombre de caractères, on affiche un message d’erreur : on ne peut pas comparer tous les caractères des deux espèces.
• Sinon, on parcourt les caractères des deux espèces.
? Si l’on trouve un caractère qui est à l’état originel dans la seconde espèce mais à l’état dérivé dans la première, alors la première espèce n’est pas l’ancêtre de la seconde : on renvoie alors False, ce qui arrête immédiatement la fonction.
? Si l’on arrive au bout de la boucle sans avoir trouvé de tel caractère, on peut conclure que tous les caractères qui sont à l’état originel dans la seconde espèce le sont aussi dans la première, donc que la première espèce est ancêtre de la seconde : on renvoie alors True.
(b) La fonctionNombre_descendantessuivante prend en entrée la liste des don- nées phylogénétiques ainsi qu’une espèce, et renvoie le nombre de descen- dantes de l’espèce donnée présentes dans la liste des données phylogéné- tiques.
Pour ce faire, on commence par créer une variablecompteur, initialisée à 0, qui comptera le nombre d’espèces descendantes de l’espèce dans la liste des données phylogénétiques.
On parcourt ensuite la liste des données phylogénétiques : à chaque fois que l’on rencontre dans la liste une espèce descendante de l’espèce donnée en argument, on incrémente le compteur.
À la fin du parcours, on renvoie la valeur du compteur.
def Nombre_descendantes(Donnees_phylo , espece):
"""
Entrées : la liste Donnees_phylo des données phylogénétiques , une espèce espece.
Sortie : le nombre d'espèces descendantes de l'espèce espece dans la liste Donnes_phylo.
"""
compteur = 0
for espece2 in Donnees_phylo:
if Est_ancetre(espece , espece2):
compteur = compteur + 1 return compteur
4. Pour déterminer si une espèce est descendante immédiate d’une autre, on procède de la manière suivante.
• Comme à la question 3.a), si les deux espèces n’ont pas le même nombre de caractères, on affiche un message d’erreur.
• Sinon, on commence par créer une variable nombre_mutations, initialisée à 0, qui comptera le nombre de mutations de la première espèce vers la se- conde, c’est-à-dire de caractères qui sont à l’état originel dans la première espèce et à l’état dérivé dans la seconde.
On parcourt ensuite les caractères des deux espèces.
? Si l’on trouve un caractère qui est à l’état originel dans la seconde espèce mais à l’état dérivé dans la première, alors la première espèce n’est pas l’ancêtre de la seconde : on renvoie alors False, ce qui arrête immédia- tement la fonction.
? Lorsque l’on rencontre un caractère qui est à l’état originel dans la première espèce et à l’état dérivé dans la seconde, on augmente de 1 le nombre de mutations nombre_mutations.
Si, à l’issue de cette incrémentation, ce nombre dépasse strictement 1, alors la descendance éventuelle n’est pas immédiate, donc on renvoie False, ce qui arrête la fonction tout de suite.
? Si l’on arrive au bout de la boucle sans avoir renvoyé False, c’est que la deuxième espèce est bien descendante de la première et que le nombre de mutations est inférieur ou égal à 1. On renvoie alors le booléen mutations == 1, qui vaut True si le nombre de mutations vaut 1 et False si les nombres de mutations vaut 0, c’est-à-dire si les deux es- pèces sont égales.
def Est_descendante_immediate(espece1 , espece2):
if len(espece1) != len(espece2):
print("Erreur ! Ces deux espèces n'ont pas le même nombre de caractères")
else:
nombre_mutations = 0
for k in range(len(espece1)):
if espece1[k] == 1 and espece2[k] == 0:
# L'espèce espece2 n'est pas descendante
# de l'espèce espece1.
return False
elif espece1[k] == 0 and espece2[k] == 1:
nombre_mutations += 1 if nombre_mutations > 1:
# S'il y a descendance ,
# elle n'est pas immédiate.
return False return (mutations == 1)