Aller au contenu

Accueil › BCPST › Informatique

BCPST1 · Informatique · semaine 1

Bases de Python et boucles simples

Variables, expressions et instructions, types de base, tests, fonctions et boucles, éléments exigibles du langage. Sommes, produits, suites récurrentes et chaînes de caractères par des boucles simples.

Sommaire

La fiche de cours

Résumé en cinq points

  1. Le programme limite Python à un noyau précis, variables, types int, float, bool, chaînes, listes et dictionnaires, if, while, for, def et return.
  2. Une boucle simple met à jour quelques variables à chaque tour. Son invariant dit ce qu'elles contiennent et guide l'initialisation.
  3. Sommes et produits utilisent un accumulateur initialisé à 0 ou à 1. Les suites d'ordre 1 se calculent avec une variable, celles d'ordre 2 avec deux et un décalage soigneux.
  4. Pour garder tous les termes d'une suite, on construit une liste par append successifs, de u₀ à un.
  5. Les chaînes sont immuables et se parcourent caractère par caractère. On construit une nouvelle chaîne pour tout résultat transformé.
Pourquoi ce chapitre

L'informatique de BCPST sert à calculer, simuler et traiter des données, en mathématiques, en physique-chimie et en biologie. Presque tout repose sur une idée simple, répéter un calcul avec une boucle en mettant à jour quelques variables. Ce chapitre fixe ce socle et le vocabulaire exact du programme. Il pose aussi des habitudes qui comptent à l'écrit comme à l'oral, écrire une fonction qui renvoie un résultat au lieu d'afficher, vérifier sur un petit cas calculé à la main, et savoir ce que contient chaque variable après chaque tour de boucle. Les suites récurrentes, calculées ici, reviennent en mathématiques et dans les modèles de dynamique des populations.

Objectifs du semestre 1 et discipline de programmation

Le programme fixe trois objectifs aux séances du premier semestre.

  • Consolider l'apprentissage de Python commencé au lycée.
  • Mettre en place un environnement de travail, éditeur, console, exécution d'un fichier.
  • Mettre en place une discipline de programmation, spécification précise des fonctions et des programmes, annotations et commentaires, jeux de tests. La consolidation porte sur six points, que la suite de la fiche reprend dans l'ordre, les variables, les expressions et instructions, les instructions conditionnelles, les fonctions, les instructions itératives et la manipulation de quelques structures de données. Aucune connaissance sur les modules rencontrés en séance n'est exigible.

Variables, expressions et instructions

Variables et affectation

Une variable est un nom qui désigne une valeur. L'affectation, écrite avec le signe =, évalue d'abord l'expression de droite puis associe sa valeur au nom de gauche. Ainsi n = n + 1 n'est pas une équation mais une mise à jour, la nouvelle valeur de n est l'ancienne plus un. Le principe d'indentation délimite les blocs, et la portée lexicale fixe où une variable est visible. Dans une fonction, Python cherche d'abord la valeur définie dans la fonction, puis, à défaut, dans l'espace global du module.

Expressions et types de base

Une expression se calcule et produit une valeur, une instruction s'exécute et modifie l'état du programme, par exemple une affectation. Les opérations exigibles de l'annexe sont les suivantes.

  • Entiers, type int, avec +, −, * et **, sur des opérandes positifs. La division entière et le reste ne figurent pas dans l'annexe de BCPST, ils peuvent être utilisés en séance avec un rappel.
  • Flottants, type float, avec +, −, *, / et **. La division / donne toujours un flottant, et les flottants sont des valeurs approchées. On ne teste jamais l'égalité de deux flottants avec ==. Par exemple, 0.1 + 0.2 == 0.3 vaut False, car aucun de ces trois nombres n'est représenté exactement en machine. On teste plutôt que la valeur absolue de la différence est inférieure à une petite tolérance.
  • Booléens, type bool, valeurs True et False, avec not, or et and.
  • Comparaisons ==, !=, <, >, <=, >=.

Instructions conditionnelles

if, elif et else exécutent un bloc selon la valeur d'une condition. Les conditions sont testées dans l'ordre et une seule branche est exécutée. Exemple, classer un pH en acide, neutre ou basique, avec trois branches sur pH < 7, pH == 7 et le reste, en gardant à l'esprit qu'avec des flottants le cas neutre exact est rare et qu'une tolérance est plus réaliste.

Fonctions

Une fonction se définit par def f(p1, …, pn), et return renvoie sa valeur. On distingue soigneusement renvoyer et afficher. Une fonction qui calcule doit renvoyer son résultat avec return, pour qu'on puisse le réutiliser dans un autre calcul. L'affichage avec print, utilisé sans paramètre facultatif, sert seulement à observer. Les commentaires, introduits par #, donnent la spécification de la fonction, ce qu'elle attend et ce qu'elle renvoie.

def somme_carres(n):
    # n : entier positif. Renvoie 1² + 2² + ... + n².
    s = 0
    for k in range(1, n + 1):
        s = s + k ** 2
    return s

Vérification sur un petit cas, somme_carres(3) vaut 1 + 4 + 9 = 14, ce que confirme la formule n(n + 1)(2n + 1)/6.

Instructions itératives

Boucle for

La boucle for répète un bloc pour chaque valeur d'une séquence, range(a, b) pour les entiers de a inclus à b exclu, une chaîne de caractères, une liste, ou un dictionnaire à travers ses méthodes keys et items. range(n) va de 0 à n − 1, soit n valeurs. C'est la boucle à utiliser quand le nombre de tours est connu à l'avance.

Boucle while

La boucle while répète un bloc tant qu'une condition est vraie. Elle convient quand on ne sait pas à l'avance combien de tours seront nécessaires, par exemple pour trouver le premier entier n tel que la somme des 1/k pour k de 1 à n dépasse 3. Deux vérifications s'imposent. La condition doit finir par devenir fausse, sinon la boucle ne s'arrête pas. Et les variables doivent être initialisées avant la boucle.

def premier_rang(seuil):
    # renvoie le plus petit n tel que 1 + 1/2 + ... + 1/n > seuil
    n = 0
    s = 0
    while s <= seuil:
        n = n + 1
        s = s + 1 / n
    return n

premier_rang(3) renvoie 11, car la somme vaut environ 2,929 pour n = 10 et 3,020 pour n = 11. La boucle s'arrête parce que cette somme tend vers l'infini, résultat démontré en mathématiques. Pour une suite convergente, une boucle while avec un seuil supérieur à la limite ne s'arrêterait jamais, d'où l'importance de savoir avant d'écrire la boucle que la condition finira par devenir fausse. Un return dans le corps d'une boucle en fait sortir immédiatement. La forme else d'une boucle n'est pas au programme.

Quelques structures de données

Chaînes de caractères

Une chaîne est une suite immuable de caractères. On connaît sa longueur avec len, on accède au caractère d'indice i avec s[i] pour i de 0 à len(s) − 1, on concatène avec +, on répète avec *, on extrait une tranche avec s[i:j], de l'indice i inclus à j exclu. Immuable signifie qu'on ne modifie pas une chaîne, on en construit une nouvelle.

Listes

Une liste est une suite modifiable. On la crée par compréhension, [e for x in s], ou par append successifs à partir de la liste vide. On dispose de len, de l'accès et de la modification par indice, de la concaténation +, de la répétition *, des tranches, de la copie et de pop en dernière position. L'affectation m = t ne copie pas la liste, m et t désignent la même, et modifier l'une modifie l'autre. Ce phénomène, appelé aliasing, est étudié au second semestre.

Dictionnaires

Un dictionnaire associe des valeurs à des clés. On le crée, on lit et on insère une valeur par sa clé, on connaît sa taille avec len et on le copie avec copy. Il est approfondi au second semestre.

Algorithmes élémentaires opérant par boucles simples

Calculs de sommes et de produits

Le schéma est toujours le même. Un accumulateur est initialisé à l'élément neutre, 0 pour une somme, 1 pour un produit, puis mis à jour à chaque tour. Après le tour d'indice k, il contient la somme ou le produit des termes jusqu'à k. Cette phrase est l'invariant de la boucle, et elle permet de vérifier le programme.

Exemple de produit, la factorielle, n! = 1 × 2 × … × n, avec un accumulateur initialisé à 1. Initialiser à 0 donne toujours 0, c'est l'erreur la plus fréquente.

Exemple de somme de flottants, la somme des 1/k² pour k de 1 à n, qui tend vers π²/6, environ 1,645. On l'écrit avec 1 / k ** 2, et la division / renvoie un flottant.

Calcul des termes d'une suite récurrente d'ordre 1

Une suite définie par u₀ et un+1 = f(un) se calcule avec une seule variable mise à jour n fois.

def terme_logistique(r, u0, n):
    # modèle logistique discret u(k+1) = r u(k) (1 - u(k)). Renvoie u(n).
    u = u0
    for k in range(n):
        u = r * u * (1 - u)
    return u

Le modèle logistique, où u est une population rapportée à sa valeur maximale, converge vers 1 − 1/r pour 1 < r < 3, oscille ensuite entre plusieurs valeurs, et devient chaotique pour r proche de 4. Le nombre de tours est range(n), n tours, car on part de u₀ et on calcule u₁, …, un. Se tromper d'un tour est l'erreur classique, qu'on détecte en testant n = 0 et n = 1 à la main.

Autre exemple, une population de poissons qui perd 10 % de ses effectifs chaque année et reçoit 10 000 alevins par alevinage, modélisée par un+1 = 0,9 un + 10 000. Cette suite arithmético-géométrique converge vers le point fixe ℓ tel que ℓ = 0,9 ℓ + 10 000, soit ℓ = 100 000, quelle que soit la population initiale. Le programme est le même que pour toute suite d'ordre 1, seule la fonction de mise à jour change, et comparer les termes calculés à la limite théorique est un bon test.

Suites récurrentes d'ordre supérieur

Pour une suite d'ordre 2, un+2 = a un+1 + b un, il faut garder les deux derniers termes. À chaque tour, on calcule le nouveau terme à partir des deux précédents, puis on décale.

def fibonacci(n):
    # F(0) = 0, F(1) = 1, F(k+2) = F(k+1) + F(k). Renvoie F(n).
    a = 0
    b = 1
    for k in range(n):
        c = a + b
        a = b
        b = c
    return a

L'ordre des affectations compte. Écrire a = b puis b = a + b ferait perdre l'ancienne valeur de a. La variable auxiliaire c conserve le nouveau terme pendant le décalage. Invariant, après k tours, a vaut F(k) et b vaut F(k + 1). Pour une suite d'ordre 3 ou plus, on garde autant de variables que l'ordre, ou une liste des derniers termes.

Liste des termes d'une suite

Pour tracer une suite ou l'étudier, on veut souvent tous ses termes et pas seulement le dernier. On part d'une liste contenant u₀ et on ajoute chaque nouveau terme avec append. Le dernier terme de la liste t s'écrit t[len(t) − 1], l'indice positif valide exigé par le programme. La liste obtenue a n + 1 éléments, u₀ à un.

def termes_logistique(r, u0, n):
    t = [u0]
    for k in range(n):
        u = t[len(t) - 1]
        t.append(r * u * (1 - u))
    return t

Pour visualiser la suite, on trace les points d'abscisse k et d'ordonnée uk avec un module graphique, par exemple import matplotlib.pyplot as plt, puis plt.plot(range(n + 1), t) et plt.show(). Aucune connaissance de ce module n'est exigible, sa documentation est fournie quand il sert. Tracer la liste des termes pour r = 2,8, r = 3,2 et r = 3,9 montre d'un coup d'œil la convergence, l'oscillation entre deux valeurs et le comportement chaotique du modèle logistique.

Boucles simples sur les chaînes de caractères

Une boucle for parcourt une chaîne caractère par caractère. Exemples en lien avec la biologie, sur une séquence d'ADN écrite avec les lettres A, C, G, T.

  • Compter les bases G et C et calculer le taux de GC, nombre de G et de C divisé par la longueur.
def taux_gc(seq):
    # seq : chaîne non vide sur l'alphabet A, C, G, T
    n = 0
    for base in seq:
        if base == 'G' or base == 'C':
            n = n + 1
    return n / len(seq)
  • Construire le brin complémentaire, en remplaçant A par T, T par A, C par G et G par C, et en concaténant les caractères obtenus dans une nouvelle chaîne, puisque la chaîne d'origine est immuable.
  • Inverser une chaîne, en ajoutant chaque caractère devant le résultat partiel, ou en parcourant les indices de la fin vers le début. Le coût de ces traitements est proportionnel à la longueur de la chaîne, un seul parcours suffit.

Méthodes

  • Avant d'écrire la boucle, écrire l'invariant en une phrase, « s contient la somme des termes déjà traités ». L'initialisation et la mise à jour en découlent.
  • Initialiser un accumulateur de somme à 0 et de produit à 1.
  • Compter les tours. range(n) fait n tours, range(1, n + 1) parcourt 1 à n.
  • Pour une suite d'ordre 2, utiliser une variable auxiliaire lors du décalage, ou vérifier l'ordre des affectations.
  • Tester sur n = 0, n = 1 et un petit cas calculé à la main.
  • Renvoyer le résultat avec return, et réserver print à l'observation.
  • N'utiliser que les éléments du langage de l'annexe. Une fonction toute faite, comme sum ou max, ne remplace pas l'algorithme demandé.

Ce qu'il faut savoir refaire en khôlle

  1. Écrire une fonction qui calcule une somme ou un produit défini par une formule, avec son invariant, et la vérifier sur un petit cas.
  2. Calculer le n-ième terme d'une suite récurrente d'ordre 1, puis la liste de ses n + 1 premiers termes.
  3. Calculer le n-ième terme d'une suite d'ordre 2 en gérant correctement le décalage des variables.
  4. Trouver avec une boucle while le premier rang où une suite dépasse un seuil, en justifiant l'arrêt de la boucle.
  5. Parcourir une séquence d'ADN pour compter des bases, calculer un taux de GC ou construire le brin complémentaire.
  6. Expliquer la différence entre renvoyer et afficher, et entre m = t et une copie de liste.

Méthodes et exercices

Les résultats à connaître

En informatique, les « résultats » sont le comportement exact du langage, la correction des algorithmes et leur nombre d'opérations dans le pire cas.

  • L'affectation évalue entièrement l'expression de droite, puis donne sa valeur au nom de gauche : exigible.
  • range(a, b) produit b − a entiers si a < b, et aucun sinon. range(n) fait donc n tours, et zéro si n ≤ 0 : exigible.
  • / renvoie toujours un flottant, même entre entiers. ** entre entiers positifs renvoie un entier exact : exigible.
  • Les flottants sont approchés, deux calculs mathématiquement égaux peuvent différer. On compare avec une tolérance : exigible, à savoir illustrer.
  • and et or n'évaluent leur second opérande que si c'est nécessaire : exigible.
  • return termine la fonction immédiatement, même au milieu d'une boucle. Une fonction qui n'exécute aucun return renvoie None : exigible.
  • Une chaîne est immuable, une liste est modifiable, et m = t ne copie pas une liste : exigible.
  • Accumulateur de somme ou de produit : après le tour d'indice k, il contient la somme ou le produit des termes d'indice au plus k : exigible avec justification par invariant.
  • Suite d'ordre 1 : après k tours, la variable contient uk. Le calcul de un demande n tours : exigible avec justification.
  • Suite d'ordre 2 : après k tours, le couple de variables vaut (uk, uk+1) : exigible avec justification.
  • Boucle while de seuil : elle termine si et seulement si la condition finit par devenir fausse. Il faut un argument mathématique ou un garde-fou : exigible avec justification.
  • Parcours d'une chaîne de longueur n : n tours de boucle, soit un nombre d'opérations proportionnel à n : exigible.
  • Hors programme signalé : division entière // et reste % (utilisables en séance avec un rappel), indices négatifs, forme else d'une boucle, fonctions sum, max et reversed, méthodes de chaîne comme count ou replace, notion formelle de complexité (vue plus tard).

Clés de démonstration

Accumulateur. On énonce l'invariant « après le tour d'indice k, s vaut la somme des termes d'indice au plus k ». L'initialisation à l'élément neutre le rend vrai avant le premier tour, car une somme vide vaut 0 et un produit vide vaut 1. L'astuce est d'écrire l'invariant avant le code : la valeur initiale et la ligne de mise à jour s'en déduisent.

Suite d'ordre 1. On raisonne par récurrence sur le nombre de tours. Avant tout tour, u vaut u₀. Si u vaut uk avant un tour, l'instruction u = f(u) lui donne la valeur uk+1. L'astuce est de compter les tours et non les indices : n tours mènent de u₀ à un.

Suite d'ordre 2. Invariant : après k tours, x vaut uk et y vaut uk+1. Le tour calcule z = a y + b x, qui vaut uk+2, puis décale. L'astuce est la variable auxiliaire, qui mémorise le nouveau terme avant d'écraser x.

Terminaison d'un while. On exhibe une quantité entière positive qui décroît strictement à chaque tour, par exemple nmax − n, ou bien on prouve mathématiquement que la suite finit par franchir le seuil. L'astuce est d'utiliser une formule explicite quand elle existe, comme pour une suite arithmético-géométrique.

Parcours de chaîne. Invariant : après avoir lu les k premiers caractères, le résultat partiel est celui qu'on aurait obtenu sur la tranche seq[0:k]. L'astuce, pour une chaîne résultat, est de décider si le nouveau caractère se place devant ou derrière le résultat partiel.

Exemples et contre-exemples à connaître

  1. Ajouter dix fois 0.1 à 0 donne 0.9999999999999999 et non 1.0. Une somme de flottants accumule les erreurs d'arrondi.
  2. range(6 / 2) provoque une erreur, car 6 / 2 vaut 3.0, un flottant. Un nombre de tours doit être un entier.
  3. Une factorielle dont l'accumulateur est initialisé à 0 renvoie 0 pour tout n. C'est le cas d'école de la mauvaise initialisation.
  4. for k in range(1, n) oublie le terme d'indice n. On le repère en testant n = 1, qui donne une somme vide.
  5. Dans Fibonacci, écrire a = b puis b = a + b fait doubler b à chaque tour. On obtient 0, 1, 2, 4, 8 au lieu de 0, 1, 1, 2, 3.
  6. Chercher le premier n tel que la somme des 1/k² dépasse 2 ne termine jamais, car cette somme reste inférieure à π²/6 ≈ 1,645.
  7. Une fonction qui affiche son résultat avec print sans le renvoyer donne None. f(3) + 1 provoque alors une erreur.
  8. Un return s indenté dans la boucle renvoie le premier terme seulement. La somme de 1 à 5 donne alors 1.
  9. seq[0] = 'T' sur une chaîne provoque une erreur. Pour transformer une séquence, on construit une nouvelle chaîne.
  10. Pour un+1 = 0,9 un + 10 000, un while qui attend u ≥ 100 000 en partant de 20 000 ne termine pas mathématiquement. La limite n'est jamais atteinte.

Techniques du chapitre

Technique 1, l'invariant d'abord. Mini-exercice : calculer le produit 1 × 3 × 5 × … × (2n − 1). Invariant : après le tour k, p est le produit des 2j + 1 pour j de 0 à k. Donc p = 1 au départ, for k in range(n) et p = p * (2 * k + 1). Tests : n = 0 donne 1, n = 2 donne 3.

Technique 2, compter les tours. Mini-exercice : for k in range(2, n) fait combien de tours ? Réponse : n − 2 si n ≥ 2, et zéro sinon. On vérifie toujours le dernier tour et le cas où la boucle ne s'exécute pas.

Technique 3, mettre à jour un terme au lieu de le recalculer. Mini-exercice : sommer qk pour k de 0 à n sans **. On garde une variable terme, initialisée à 1, et on fait s = s + terme puis terme = terme * q. Chaque tour coûte deux opérations.

Technique 4, un while avec justification. Mini-exercice : plus petit n tel que 2n > 10⁶. On part de n = 0 et p = 1, et tant que p ≤ 10⁶ on double p et on incrémente n. La suite 2n tend vers l'infini, donc la boucle termine. Réponse : 20, car 220 = 1 048 576.

Technique 5, comparer des flottants avec une tolérance. Mini-exercice : tester que x et y sont égaux à 10⁻⁹ près sans fonction de bibliothèque. On écrit x - y < 1e-9 and y - x < 1e-9, ce qui équivaut à |x − y| < 10⁻⁹.

Technique 6, construire une chaîne résultat. Mini-exercice : transcrire un brin codant d'ADN en ARN, en remplaçant T par U. On part de r = '' et, pour chaque caractère c, on ajoute 'U' si c vaut 'T', sinon c lui-même, avec r = r + .... 'GAT' donne 'GAU'.

Technique 7, tracer à la main. Mini-exercice : suivre fibonacci(4) de la fiche. On écrit a et b après chaque tour : (1, 1), (1, 2), (2, 3), (3, 5). La fonction renvoie a = 3, qui est bien F(4).

Exercices classiques corrigés

Choisis ton niveau. Difficulté indicative ; les exercices qui se suivent restent dans l'ordre.

Exercice 1

Classiques de khôlle et concours · Khôlle

Calculer la somme des 1/(k(k + 1)) pour k de 1 à n et vérifier qu'elle vaut 1 − 1/(n + 1) à 10⁻⁹ près.

Technique accumulateur flottant et comparaison avec tolérance.

Voir le corrigé
  1. Idée : on somme les termes, puis on compare au résultat donné par télescopage, car 1/(k(k + 1)) = 1/k − 1/(k + 1).
  2. Code :
def verifie(n):
    # n : entier >= 1. True si la somme calculée vaut 1 - 1/(n+1) à 1e-9 près.
    s = 0
    for k in range(1, n + 1):
        s = s + 1 / (k * (k + 1))
    e = s - (1 - 1 / (n + 1))
    return e < 1e-9 and -e < 1e-9
  1. Test : n = 1 donne s = 0,5 = 1 − 1/2. Le calcul fait n tours.

piège : Écrire s == 1 - 1 / (n + 1), qui peut renvoyer False à cause des arrondis.

Exercice 2

Classiques de khôlle et concours · Khôlle

Calculer la somme des xk/k! pour k de 0 à n, approximation de exp(x), sans recalculer de factorielle.

Technique mise à jour d'un terme.

Voir le corrigé
  1. Idée : xk/k! s'obtient à partir du terme précédent en multipliant par x/k.
  2. Code :
def approx_exp(x, n):
    # n : entier >= 0. Renvoie la somme des x**k / k! pour k de 0 à n.
    terme = 1
    s = 1
    for k in range(1, n + 1):
        terme = terme * x / k
        s = s + terme
    return s
  1. Invariant : après le tour k, terme vaut xk/k! et s vaut la somme jusqu'à l'indice k.
  2. Test : n = 0 donne 1, n = 1 donne 1 + x. approx_exp(1, 10) vaut environ 2,7182818, proche de e.
  3. Coût : n tours de trois opérations. Recalculer k! à chaque tour ferait environ n²/2 multiplications.

piège : Initialiser s à 0 avec une boucle qui commence à k = 1, ce qui oublie le terme 1.

Exercice 3. Pour u₀ donné et uk+1 = 0,5 uk + 3, renvoyer u₀ + u₁ + … + un

Classiques de khôlle et concours · Khôlle

Technique deux variables mises à jour dans la même boucle.

Voir le corrigé
  1. Idée : u parcourt la suite, s cumule les termes. On ajoute chaque nouveau terme juste après l'avoir calculé.
  2. Code :
def somme_termes(u0, n):
    # renvoie u(0) + ... + u(n) pour u(k+1) = 0.5 u(k) + 3
    u = u0
    s = u0
    for k in range(n):
        u = 0.5 * u + 3
        s = s + u
    return s
  1. Invariant : après k tours, u vaut uk et s vaut u₀ + … + uk.
  2. Test : avec u₀ = 0, les termes sont 0, 3, 4,5, donc somme_termes(0, 2) vaut 7,5.

piège : Initialiser s à 0 et ajouter u avant la mise à jour. On obtient u₀ + … + un−1, soit 3 au lieu de 7,5.

Exercice 4. Pour uk+2 = a uk+1 + b uk, avec u₀ et u₁ donnés, renvoyer un pour tout n ≥ 0

Classiques de khôlle et concours · Khôlle

Technique décalage avec variable auxiliaire.

Voir le corrigé
  1. Idée : on garde les deux derniers termes, et on renvoie le premier des deux pour traiter n = 0 sans cas particulier.
  2. Code :
def terme_ordre2(a, b, u0, u1, n):
    # renvoie u(n), n entier >= 0
    x = u0
    y = u1
    for k in range(n):
        z = a * y + b * x
        x = y
        y = z
    return x
  1. Invariant : après k tours, x = uk et y = uk+1.
  2. Test : a = 1, b = 2, u₀ = 0, u₁ = 1 donne 0, 1, 1, 3, 5, 11. Cela concorde avec la formule (2n − (−1)n)/3.

piège : Boucler sur range(n - 1) et renvoyer y. Pour n = 0, la boucle ne tourne pas et on renvoie u₁.

Exercice 5

Classiques de khôlle et concours · Khôlle

Renvoyer la liste [u₀, …, un] pour u₀ = 0, u₁ = 0, u₂ = 1 et uk+3 = uk+2 + uk+1 + uk.

Technique liste des termes, lecture des derniers par indices positifs.

Voir le corrigé
  1. Idée : on part des trois termes initiaux et on ajoute des termes jusqu'à en avoir n + 1.
  2. Code :
def liste_ordre3(n):
    # n : entier >= 0. Renvoie [u(0), ..., u(n)].
    t = [0, 0, 1]
    for k in range(n - 2):
        m = len(t)
        t.append(t[m - 1] + t[m - 2] + t[m - 3])
    return t[0:n + 1]
  1. Compte : on veut n + 1 éléments et on en a 3, donc n − 2 ajouts. Pour n < 2, la boucle ne tourne pas.
  2. Test : liste_ordre3(5) vaut [0, 0, 1, 1, 2, 4].

piège : Renvoyer t sans la tranche finale. Pour n = 0 ou n = 1, on obtient [0, 0, 1], trois termes au lieu de n + 1.

Exercice 6

Classiques de khôlle et concours · Type écrit G2E

Une population de poissons suit un+1 = 0,9 un + 10 000. Renvoyer le premier rang n tel que un ≥ seuil, et justifier l'arrêt de la boucle.

Technique while de seuil justifié par une formule explicite.

Voir le corrigé
  1. Code :
def annee_seuil(u0, seuil):
    # plus petit n tel que u(n) >= seuil. Hypothèse : seuil < 100000.
    n = 0
    u = u0
    while u < seuil:
        u = 0.9 * u + 10000
        n = n + 1
    return n
  1. Terminaison : un − 100 000 = 0,9n (u₀ − 100 000). Si u₀ ≥ seuil, on renvoie 0. Sinon u₀ < 100 000 et un croît vers 100 000, qui dépasse le seuil, donc le seuil est franchi en un nombre fini d'années.
  2. Test : annee_seuil(20000, 90000) renvoie 20. En effet u₁₉ ≈ 89 193 et u₂₀ ≈ 90 274.
  3. Invariant : u vaut un et tous les termes précédents sont sous le seuil, donc n est bien le premier rang.

piège : Ne pas imposer seuil < 100 000. Mathématiquement la boucle ne s'arrête jamais, et en flottants le résultat dépend des arrondis.

Exercice 7

Classiques de khôlle et concours · Khôlle

Renvoyer le brin complémentaire inversé d'une séquence d'ADN. C'est le second brin lu dans son propre sens de lecture.

Technique chaîne résultat construite par la gauche.

Voir le corrigé
  1. Idée : on complémente chaque base et on la place devant le résultat partiel, ce qui inverse l'ordre.
  2. Code :
def complement_inverse(seq):
    # seq : chaîne sur A, C, G, T
    r = ''
    for base in seq:
        if base == 'A':
            c = 'T'
        elif base == 'T':
            c = 'A'
        elif base == 'C':
            c = 'G'
        else:
            c = 'C'
        r = c + r
    return r
  1. Test : 'AACG' donne successivement 'T', 'TT', 'GTT', 'CGTT'.
  2. Coût : n tours. Chaque concaténation fabrique une nouvelle chaîne, point qui sera étudié plus tard.

piège : Écrire r = r + c, qui donne le complémentaire sans l'inverser. Le else suppose aussi que la séquence ne contient que A, C, G, T.

Exercice 8

Classiques de khôlle et concours · Type écrit Agro-Véto

Compter les positions où un motif non vide m apparaît dans une séquence seq, chevauchements compris.

Technique parcours par indices et tranches.

Voir le corrigé
  1. Idée : m apparaît en position i si la tranche seq[i:i + p] vaut m, avec p = len(m). La dernière position possible est len(seq) − p.
  2. Code :
def occurrences_motif(seq, m):
    # m non vide. Nombre de i tels que seq[i:i+len(m)] == m.
    p = len(m)
    c = 0
    for i in range(len(seq) - p + 1):
        if seq[i:i + p] == m:
            c = c + 1
    return c
  1. Tests : ('AAAA', 'AA') donne 3, ('TATATA', 'TATA') donne 2. Si le motif est plus long que la séquence, la boucle ne tourne pas et on renvoie 0.
  2. Coût : n − p + 1 comparaisons de tranches de longueur p, soit environ (n − p + 1) × p comparaisons de caractères.

piège : Écrire range(len(seq) - p), qui oublie la dernière position. ('AAAA', 'AA') donne alors 2.

Exercice 9

Classiques de khôlle et concours · Type écrit Agro-Véto

Méthode de Héron : pour a > 0, u₀ = a et un+1 = (un + a/un)/2. Itérer jusqu'à ce que deux termes consécutifs diffèrent de moins de eps.

Technique while sur l'écart entre deux termes.

Voir le corrigé
  1. Idée : on garde deux termes consécutifs u et v. On admet du cours de mathématiques que la suite converge vers √a, donc l'écart tend vers 0.
  2. Code :
def heron(a, eps):
    # a > 0, eps > 0 pas trop petit, 1e-12 par exemple
    u = a
    v = (u + a / u) / 2
    while u - v > eps or v - u > eps:
        u = v
        v = (u + a / u) / 2
    return v
  1. Test : heron(2, 1e-12) donne 1,41421356237… en cinq tours environ, car la convergence est très rapide.
  2. Précision : les flottants ont environ 16 chiffres significatifs. Un eps trop petit devant a peut empêcher l'arrêt si deux flottants voisins alternent.

piège : Ne tester que u - v > eps. Pour a = 0,25, on a v = 0,625 > u au départ, la boucle ne tourne pas et on renvoie 0,625 au lieu de 0,5.

Exercice 10

Classiques de khôlle et concours · Type écrit Agro-Véto

Une population compte jn juvéniles et an adultes, avec jn+1 = 1,2 an et an+1 = 0,5 jn + 0,6 an. Renvoyer la première année où l'effectif total atteint le double de l'effectif initial, ou −1 si ce n'est pas le cas avant nmax années.

Technique mise à jour simultanée et garde-fou.

Voir le corrigé
  1. Idée : les deux nouvelles valeurs se calculent à partir des anciennes. On sauve la nouvelle valeur de j avant de modifier a. Le compteur n borne le nombre de tours.
  2. Code :
def annee_doublement(j0, a0, nmax):
    j = j0
    a = a0
    n = 0
    while j + a < 2 * (j0 + a0) and n < nmax:
        nouveau_j = 1.2 * a
        a = 0.5 * j + 0.6 * a
        j = nouveau_j
        n = n + 1
    if j + a >= 2 * (j0 + a0):
        return n
    return -1
  1. Terminaison : nmax − n est un entier positif qui décroît de 1 à chaque tour. Sans garde-fou, une population qui décline ferait tourner la boucle indéfiniment.
  2. Test : avec j₀ = 0 et a₀ = 100, les totaux sont 100, 180, 168, puis 208,8. La fonction renvoie 3.

piège : Modifier j puis calculer a avec le nouveau j. Dès la première année, a vaudrait alors 120 et le total 240, d'où une réponse fausse, 1.

Erreurs fréquentes

  1. Mal initialiser un accumulateur : 0 pour un produit, 0 au lieu du premier terme pour une somme qui commence à u₀. Toujours tester n = 0.
  2. Se tromper d'un tour dans range, en particulier pour une liste de termes, qui doit contenir n + 1 éléments de u₀ à un.
  3. Écraser une variable dont on a encore besoin, dans une suite d'ordre 2 ou un système de deux suites. Une variable auxiliaire règle le problème.
  4. Écrire un while sans justifier qu'il termine, ou comparer des flottants avec ==. Donner un argument mathématique ou un garde-fou, et une tolérance.
  5. Afficher au lieu de renvoyer, placer le return dans la boucle, ou utiliser sum, max, // et % là où l'annexe demande d'écrire la boucle soi-même.

Fiche de cours rédigée à partir du programme officiel d'informatique de BCPST (2021), programme du semestre 1, et de son annexe « Langage Python », qui liste limitativement les éléments du langage exigibles. Elle couvre les objectifs du premier semestre, la consolidation du langage autour des variables, des expressions et instructions, des instructions conditionnelles, des fonctions, des instructions itératives et de quelques structures de données, puis le premier thème du semestre, les algorithmes élémentaires opérant par boucles simples, sommes et produits, termes d'une suite récurrente d'ordre 1 ou supérieur, liste des termes et chaînes de caractères. L'ordre des thèmes du semestre n'est pas imposé par le programme.