Vidéo 30 : La boucle while

\(\newcommand{\ds}{\displaystyle}\) \(\newcommand{\Frac}{\ds\frac}\) \(\renewcommand{\r}{\mathbb{ R}}\) \(\newcommand{\C}{\mathbb{ C}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\z}{\mathbb{ Z}}\) \(\newcommand{\Q}{\mathbb{ Q}}\) \(\newcommand{\N}{\mathbb{ N}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\ol}{\overline}\) \(\newcommand{\abs}[1]{\left| \,{#1} \right|}\) \(\newcommand{\pv}{\;;\;}\) \(\newcommand{\ens}[1]{\left\{ {#1} \right\}}\) \(\newcommand{\mens}[1]{\setminus\left\{ {#1} \right\}}\) \(\newcommand{\Par}[1]{\left({#1}\right)}\)

Vidéo 30 : La boucle while

Boucle while

Un problème typique

Étant donné un entier \(n\), on cherche un entier noté \(m\) qui soit le premier multiple de 10 supérieur ou égal à \(n\). Si par exemple \(n=42\) alors \(m=50\) : en effet, 50 est bien un multiple de 10 et il n’y en pas d’autre entre 42 et 50. Voici d’autres exemples :

\(n\) 2038 420 0
\(m\) 2040 420 0

L’idée à implémenter

Les multiples de 10 sont les entiers : \(0, 10, 20, 30\) etc. Ils s’écrivent sous la forme \(10k\)\(k\) varie à partir de 0.

Pour trouver \(m\), on parcourt les multiples de 10 depuis 0 et on s’arrête une fois qu’on a atteint ou dépassé l’entier \(n\) donné. Autrement dit, on énumère les multiples \(m\) de 10 tant que \(m < n\). Le parcours est montré dans le tableau ci-dessous où on suppose que \(n=42\).

\(k\) 0 1 2 3 4 5
\(10k < 42\) ? oui oui oui oui oui non

Implémentation en Python

La notion de « dès que », « une fois que » ou de « tant que » est traduite dans le code Python par l’instruction while.

Le code suivant répond au problème posé :

while_typique.py

1
2
3
4
5
6
7
n = 42
k = 0

while 10 * k < n:
    k = k + 1

print(10 * k)
8
50

On voit (cf. lignes 7 et 8) que la solution au problème est 50.

Analyse de code

On passe en revue le code de while_typique.py.

Vocabulaire de la boucle while

  • Lignes 4-5 : ces deux lignes forment une seule et même instruction, dite instruction while.
  • Ligne 4 : l”en-tête de la boucle while est la partie qui commence avec while et se termine avant le séparateur : (deux-points).
  • Ligne 5 : le corps de la boucle while qui est le bloc indenté sous les deux-points.

Répétition

Une boucle while répète une action (ligne 5) tant qu’une condition, ici (cf. ligne 4) :

10 * k < n

reste vraie. Plus précisément,

  • si la condition est vraie, l’exécution entre dans le corps de la boucle et à la fin du corps de la boucle, la condition est à nouveau testée (d’où le terme de boucle)
  • si la condition est fausse, l’exécution du programme va au-delà du corps de la boucle, ici à la ligne 6.

Variable de contrôle

Une boucle while fait souvent appel à une variable de contrôle, ici k, qui évolue pendant la boucle. Typiquement,

  • cette variable est initialisée avant la boucle, ici ligne 2 ;
  • cette variable est réaffectée, dans le corps de la boucle while, ici par l’instruction ligne 5 ;
  • la condition à tester (ici ligne 4) est différente d’un tour de boucle à l’autre car elle dépend de k qui, entre-temps, a varié.

Remarques

  • Ligne 4 : dans l’immense majorité des cas, le corps d’une boucle while est indenté.
  • Ligne 1 : il est possible de faire d’autres essais de code en changeant juste la valeur de \(n\). Par exemple, si on change \(n\) en 2038, le programme affichera 2040.
  • Il se peut que l’exécution du code n’entre même pas dans le corps de la boucle while. Par exemple, si ligne 1, on choisit n=0 au lieu de n=42, le test 10 * k < n (ligne 4) échoue immédiatement et donc le programme continue à la ligne 6 sans même passer par la ligne 5.
  • À la différence de la syntaxe des boucles while du C et de Java, le booléen évalué avant chaque tour de boucle n’a pas besoin d’être entouré de parenthèses.

Comprendre comment s’exécute une boucle while

Modifions le code précédent while_typique.py pour mieux comprendre l’exécution de la boucle while:

while_typique_affichage.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
n = 42
k = 0
print("avant while", "-> k=", k)
print()

while 10 * k < n:
  print("debut while", "k=", k, "-> 10*k=", 10*k)
  k = k + 1
  print("fin while", "-> k=", k)
  print()
print("apres while")
print(10 * k)
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
avant while -> k= 0

debut while k= 0 -> 10*k= 0
fin while -> k= 1

debut while k= 1 -> 10*k= 10
fin while -> k= 2

debut while k= 2 -> 10*k= 20
fin while -> k= 3

debut while k= 3 -> 10*k= 30
fin while -> k= 4

debut while k= 4 -> 10*k= 40
fin while -> k= 5

apres while
50

En plus du code initial, while_typique_affichage.py contient des instructions d’affichage (lignes 3, 7, 9 et 11) et des instructions de sauts de ligne (lignes 4 et 10) dans la sortie pour observer l’évolution de k et de 10 * k avant, pendant et après la boucle while.

L’exécution du programme est la suivante :

  • Lignes 2 et 7 : la valeur de k avant le commencement de la boucle while
  • Ligne 6 : k vaut 0. Le test de la boucle while est effectué : a-t-on 0 < 42 ? La réponse est oui donc, l’exécution du code continue dans le corps de la boucle while
  • Les affichages sont effectués : k est changé de 0 à 1 (lignes 8 et 16).
  • Ligne 6 : le test de la boucle while est à nouveau effectué : a-t-on \(10 < 42\) ? La réponse est oui et l’exécution entre à nouveau dans le corps de la boucle. Cette opération se répète jusqu’à ce que k = 5 (ligne 8 et ligne 28).
  • Ligne 6 : le test de la boucle while est effectué : a-t-on \(50 < 42\) ? Cette fois, la réponse est non donc l’exécution quitte la boucle et continue lignes 11 et 12, cf. lignes 30 et 31.
  • Ligne 12 : le résultat affiché (cf. ligne 31) est bien le résultat demandé. Comme le test 10 * k < n a échoué pour la première fois, c’est qu’on a 10 * k >= n avec k minimal et c’est bien ce que l’on cherchait.

Calculer le nombre de chiffres d’un entier

On donne un entier \(\mathtt{n\geq 0}\) et on cherche le nombre de chiffres de \(\mathtt{n}\) (ce nombre de chiffres sera appelé nchiffres). Par exemple, si \(\mathtt{n = 2020}\) alors nchiffres = 4 ou encore si \(\mathtt{n = 42}\) alors nchiffres = 2.

L’idée pour calculer nchiffres est de compter le nombre de divisions successives de n par 10 jusqu’à ce que le quotient (entier) soit nul. Par exemple

  • le quotient entier de 2020 par 10 est 202,
  • le quotient entier de 202 par 10 est 20,
  • le quotient entier de 20 par 10 est 2,
  • le quotient entier de 2 par 10 est 0.

C’est parce 2020 a justement 4 chiffres qu’on a effectué 4 divisions successives par 10 avant d’obtenir un quotient nul.

Ecrire un code Python utilisant une boucle while et qui détermine nchiffres connaissant \(\mathtt{n}\).

Voici quelques exemples de comportements

42 -> 2
2020 -> 4
10 -> 2
7 -> 1
0 -> 1
741520036365253625145211741523636854198541 -> 42

Premier entier pair

Ecrire un code qui à partir d’une liste L d’entiers :

  • affiche le premier entier pair rencontré dans la liste L si L contient au moins un entier pair,
  • affiche le nombre \(-1\) s’il n’existe aucun entier pair dans la liste.

Voici quelques exemples de comportement du programme :

[1, 3, 42, 51, 32, 9] -> 42
[1, 3, 42, 51, 33] -> 42
[1, 3, 51, 33] -> -1
[1, 3, 51, 33, 42] -> 42
[42, 82] -> 42
[42] -> 42
[81] -> -1
[] -> -1

Solution

Version for

L=[1, 3, 41, 51, 31, 9]
ok=False
i=0
n=len(L)

for i in range(n):
    if L[i]%2==0 and not ok:
        print(L[i])
        ok=True
if not ok:
    print(-1)

Il suffit d’appliquer la technique vue dans le paragraphe Boucle while et parcours de liste.

L=[1, 3, 42, 51, 32, 9]

i=0
n=len(L)

while i<n and L[i]%2 == 1:
    i = i + 1

if (i==n):
    print(L, "->", -1)
else:
    print(L, "->", L[i])
[1, 3, 42, 51, 32, 9] -> 42
  • Lignes 6-7 : la boucle while parcourt la liste L jusqu’à ce que la liste soit épuisée ou bien qu’un élément pair soit rencontrée.
  • Lignes 9-10 : en sortie de boucle, ou bien l’indice i est hors de la liste et dans ce cas la liste ne contient aucun élément pair (si un élément pair est présent uniquement en dernière position alors, en sortie de boucle, i vaut n-1.

Pour traiter le cas de plusieurs listes, au lieu de recopier du code, on place les liste dans une liste tests et on parcourt la liste tests pour tester chacune des lignes :

tests = [
[1, 3, 42, 51, 32, 9],
[1, 3, 42, 51, 33],
[1, 3, 51, 33],
[1, 3, 51, 33, 42],
[42, 82],
[42],
[81],
[]]

for L in tests:
    i=0
    n=len(L)

    while i<n and L[i]%2 == 1:
        i = i + 1

    if (i==n):
        print(L, "->", -1)
    else:
        print(L, "->", L[i])
[1, 3, 42, 51, 32, 9] -> 42
[1, 3, 42, 51, 33] -> 42
[1, 3, 51, 33] -> -1
[1, 3, 51, 33, 42] -> 42
[42, 82] -> 42
[42] -> 42
[81] -> -1
[] -> -1

La petite exponentielle contre le grand produit

Trouver le plus grand entier \(n >0\) tel que \(1.0001^n<10000n\) (on trouvera \(n=214893\)).

Solution

Posons a = 1.0001. Comme a est très proche de 1, les puissances de a augmentent mais très lentement. Par exemple :

a=1.0001
print(a**10000)
2.7181459268249255

Toutefois, comme a est strictement plus grand que 1, le nombre \(\mathtt{a^n}\) peut être rendu aussi grand que l’on veut en choisissant \(\mathtt{n}\) assez grand.

Pour le code lui-même, l’idée est très simple : on compare \(\mathtt{a^n}\) et \(\mathtt{10000\times n}\) en faisant varier \(\mathtt{n}\) suivant les entiers consécutifs jusqu’à ce que le premier dépasse le second. D’où le code :

k=1

while 1.0001**k < 10000*k:
    k=k+1

print("n =", k-1)
n = 214893
  • Ligne 1 : l’énoncé demande de trouver un entier \(\mathtt{k > 0}\).
  • Ligne 6 : en sortie de boucle while l’entier k trouvé est le premier qui vérifie \(\mathtt{1.0001^k \geq 10000\times k}\). C’est l’entier k précédent qui est tel que l’en-tête de la boucle while est vrai. C’est donc lui la solution du problème.

On peut bien sûr vérifier que le résultat n = 214893 est conforme :

k = 214893
print(k, "->", 1.0001**k < 10000*k)

k = 214894
print(k, "->", 1.0001**k < 10000*k)
214893 -> True
214894 -> False