Moon Arrows Sun
Arrows

Problèmes sur la pensée informatique

Une variable garde ce qu'on y a mis en dernier : dérouler le programme est le seul moyen de la suivre.

Dérouler un programme

On exécute le programme suivant :

a = 4
b = 10
a = a + 3
b = b - a
print(a, b)
  1. Quelle est la valeur de la variable a à la fin du programme ?

    a vaut d'abord \(4\), puis la troisième ligne lui ajoute \(3\) :

    $$ a = 7 $$
  2. Quelle est la valeur de la variable b à la fin de ce même programme ?

    Quand la quatrième ligne s'exécute, a vaut déjà \(7\), et non plus \(4\) :

    $$ b = 10 - 7 $$
    $$ b = 3 $$
  3. Expliquer pourquoi la ligne a = a + 3 n'est pas une égalité mathématique.

    Comme égalité, \(a = a + 3\) serait toujours fausse. C'est une affectation : le programme prend la valeur actuelle de a, lui ajoute \(3\), et range le résultat dans a.

Une instruction conditionnelle

Un club de sport applique le tarif suivant, écrit sous forme de programme :

if age < 12:
    prix = 15
else:
    prix = 25
print(prix)
  1. Quel prix affiche le programme pour un enfant de \(9\) ans ? pour une personne de \(12\) ans ?

    Pour \(9\) ans, la condition age < 12 est vraie. Pour \(12\) ans, elle est fausse : \(12\) n'est pas strictement inférieur à \(12\).

    $$ 9 \ \text{ans} : 15 \hspace{2em} \text{et} \hspace{2em} 12 \ \text{ans} : 25 $$
  2. Le club veut appliquer le tarif réduit jusqu'à \(12\) ans inclus. Quelle condition faut-il écrire ?

    Il faut une comparaison large, qui accepte l'égalité :

    if age <= 12:

    La condition age < 13 convient aussi, puisque l'âge est un nombre entier.

  3. Le club garde ce tarif réduit jusqu'à \(12\) ans inclus, et ajoute un tarif de \(20 \ \text{€}\) pour les personnes de \(13\) à \(17\) ans. Réécrire le programme complet.
    if age <= 12:
        prix = 15
    elif age <= 17:
        prix = 20
    else:
        prix = 25
    print(prix)

    Le programme s'arrête au premier cas vérifié : la deuxième condition n'est examinée que pour les personnes de plus de \(12\) ans.

Modifier un programme

Le programme suivant teste un nombre entier :

n = 18
if n % 3 == 0:
    print("oui")
else:
    print("non")
  1. Qu'affiche ce programme ? Que teste-t-il exactement ?

    n % 3 donne le reste de la division euclidienne de n par \(3\). Pour \(18 = 3 \times 6\), ce reste vaut \(0\) :

    $$ \text{le programme affiche « oui »} $$

    Il teste si n est un multiple de \(3\).

  2. Quelle modification de ce même programme permet de tester si n est pair ?

    Un nombre est pair quand le reste de sa division par \(2\) vaut \(0\). On remplace n % 3 par n % 2 :

    if n % 2 == 0:
  3. Écrire un programme qui affiche « multiple de 6 » lorsque le nombre est à la fois pair et multiple de \(3\).

    Les deux tests doivent être vrais en même temps : on les relie par and.

    n = 18
    if n % 2 == 0 and n % 3 == 0:
        print("multiple de 6")

    On peut aussi placer un second if à l'intérieur du premier.

Remonter un programme

Dans le programme suivant, la valeur de départ de x a été effacée. Le programme affiche \(11\).

x = ...
x = x * 2
x = x - 5
print(x)
  1. Retrouver la valeur de départ de x.

    On remonte le programme en appliquant les opérations inverses, dans l'ordre inverse :

    $$ 11 \longrightarrow 11 + 5 = 16 \longrightarrow 16 \div 2 = 8 $$
    $$ x = 8 $$

    On vérifie : \(8 \times 2 - 5 = 11\).

  2. Sami échange les deux lignes du milieu de ce programme, et garde \(8\) comme valeur de départ. Qu'affiche son programme ?

    x = x - 5 s'exécute maintenant en premier :

    $$ 8 \longrightarrow 8 - 5 = 3 \longrightarrow 3 \times 2 = 6 $$
    $$ 6 $$

    L'ordre des lignes change le résultat.

  3. Quelle valeur de départ faudrait-il au programme de Sami pour afficher, lui aussi, \(11\) ?

    On remonte le programme de Sami, dont la dernière opération est maintenant la multiplication :

    $$ 11 \longrightarrow 11 \div 2 = 5.5 \longrightarrow 5.5 + 5 = 10.5 $$
    $$ x = 10.5 $$

    On vérifie : \((10.5 - 5) \times 2 = 11\).

Les frais de livraison

Un site de vente en ligne calcule le prix à payer, en euros, à partir du montant total du panier :

if total >= 50:
    frais = 0
elif total >= 30:
    frais = 3
else:
    frais = 6
print(total + frais)
  1. Dérouler le programme pour un panier de \(25 \ \text{€}\), puis de \(30 \ \text{€}\), puis de \(49 \ \text{€}\).

    Pour \(25\), les deux conditions sont fausses. Pour \(30\) et \(49\), seule la seconde est vraie.

    $$ 25 + 6 = 31 \ \text{€} \hspace{2em} 30 + 3 = 33 \ \text{€} \hspace{2em} 49 + 3 = 52 \ \text{€} $$
  2. Léna a un panier de \(48 \ \text{€}\). Elle hésite à ajouter un stylo à \(3 \ \text{€}\). Trancher : combien lui coûte vraiment ce stylo ?

    Sans le stylo, le panier de \(48\) paie \(3\) de frais. Avec le stylo, il atteint \(51\) et la livraison devient gratuite :

    $$ 48 + 3 = 51 \ \text{€} \hspace{2em} \text{et} \hspace{2em} 51 + 0 = 51 \ \text{€} $$
    $$ \text{le stylo ne lui coûte rien de plus} $$
  3. Un programmeur a écrit les deux premières conditions dans l'ordre inverse : d'abord total >= 30, puis total >= 50. Qu'affiche son programme pour un panier de \(60 \ \text{€}\) ? Expliquer.

    Pour \(60\), la condition total >= 30 est déjà vraie : le programme prend frais = 3 et n'examine jamais la suivante.

    $$ 60 + 3 = 63 \ \text{€} $$

    La livraison gratuite n'est plus jamais accordée : la condition la plus exigeante doit être testée en premier.

  4. Avec le programme de départ, chercher tous les montants de panier inférieurs à \(50 \ \text{€}\) pour lesquels on paie plus cher qu'avec un panier de \(50 \ \text{€}\) tout rond.

    Un panier de \(50 \ \text{€}\) ne paie pas de frais : il coûte \(50 \ \text{€}\). On examine les deux autres cas du programme.

    En dessous de \(30 \ \text{€}\), on paie moins de \(30 + 6 = 36 \ \text{€}\) : jamais plus de \(50 \ \text{€}\).

    De \(30 \ \text{€}\) à moins de \(50 \ \text{€}\), on paie le panier plus \(3 \ \text{€}\). C'est plus que \(50 \ \text{€}\) lorsque le panier dépasse \(50 - 3 = 47 \ \text{€}\) :

    $$ \text{les paniers de plus de } 47 \ \text{€} \ \text{et de moins de } 50 \ \text{€} $$

    Un panier de \(47 \ \text{€}\) tout juste coûte exactement \(50 \ \text{€}\), et n'en fait pas partie.

Le chiffre de contrôle

Le numéro d'une carte de médiathèque a cinq chiffres, a, b, c, d et e. Comme sur un code-barres, le dernier est un chiffre de contrôle : à chaque saisie, la borne exécute ce programme.

Une carte de médiathèque portant le numéro 4 7 2 5 8 ; ses cinq chiffres sont nommés a, b, c, d et e, et le dernier, le chiffre de contrôle, est en vert carte de médiathèque
\(a\)
\(4\)
\(b\)
\(7\)
\(c\)
\(2\)
\(d\)
\(5\)
\(e\)
\(8\)
contrôle
s = a + 3 * b + c + 3 * d + e
if s % 10 == 0:
    print("numéro valide")
else:
    print("erreur de saisie")
  1. Dérouler le programme pour le numéro \(47258\) de la carte, puis pour le numéro \(47253\).
    $$ 4 + 3 \times 7 + 2 + 3 \times 5 + 8 = 4 + 21 + 2 + 15 + 8 = 50 $$

    Le reste de \(50\) par \(10\) vaut \(0\). Pour \(47253\), seul e change : s vaut \(45\), de reste \(5\).

    $$ 47258 : \text{« numéro valide »} \hspace{2em} \text{et} \hspace{2em} 47253 : \text{« erreur de saisie »} $$
  2. Sur une autre carte, les quatre premiers chiffres sont \(3\), \(8\), \(1\) et \(6\), et le chiffre de contrôle est effacé. Trouver toutes ses valeurs possibles, et prouver qu'il n'y en a qu'une.
    $$ s = 3 + 3 \times 8 + 1 + 3 \times 6 + e = 46 + e $$

    e est un chiffre, de \(0\) à \(9\) : s va de \(46\) à \(55\). Un seul de ces nombres est un multiple de \(10\), c'est \(50\).

    $$ e = 4 $$

    Vérification : le numéro \(38164\) donne \(s = 50\), il est valide.

  3. En tapant un numéro valide, l'hôtesse se trompe sur un seul chiffre. Prouver que le programme affiche toujours « erreur de saisie ». On pourra noter \(k\) l'écart entre le chiffre tapé et le bon chiffre, et chercher de combien change s.

    L'écart \(k\) est un nombre entier entre \(- 9\) et \(9\), différent de \(0\). Si l'erreur porte sur a, c ou e, s change de \(k\) ; si elle porte sur b ou d, multipliés par \(3\), s change de \(3k\).

    s était un multiple de \(10\) ; il le reste seulement si ce changement est lui-même un multiple de \(10\). Or \(k\) n'en est pas un, et \(3k\) non plus :

    $$ 3 \hspace{2em} 6 \hspace{2em} 9 \hspace{2em} 12 \hspace{2em} 15 \hspace{2em} 18 \hspace{2em} 21 \hspace{2em} 24 \hspace{2em} 27 $$

    Aucun de ces nombres, ni leurs opposés, ne se termine par \(0\).

    $$ \text{toujours « erreur de saisie »} $$
  4. Une autre erreur fréquente échange deux chiffres voisins. Le programme repère-t-il \(47258\) tapé \(42758\) ? Trouver tous les échanges de deux chiffres voisins qu'il ne repère pas, et prouver qu'il n'y en a pas d'autre.
    $$ 4 + 3 \times 2 + 7 + 3 \times 5 + 8 = 40 $$

    Le programme affiche « numéro valide » : l'erreur passe inaperçue.

    Dans deux chiffres voisins, l'un est multiplié par \(1\), l'autre par \(3\). On note \(x\) le chiffre multiplié par \(1\) et \(y\) celui multiplié par \(3\) : ils comptent pour \(x + 3y\) dans s. Après l'échange, ils comptent pour \(3x + y\). Le changement vaut :

    $$ 3x + y - (x + 3y) = 3x + y - x - 3y = 2x - 2y = 2(x - y) $$

    L'échange passe inaperçu si \(2(x - y)\) est un multiple de \(10\). Pour deux chiffres différents, \(x - y\) va de \(- 9\) à \(9\), sans \(0\), et \(2(x - y)\) de \(- 18\) à \(18\) : seuls \(- 10\) et \(10\) conviennent, donc \(x - y = - 5\) ou \(x - y = 5\).

    $$ \text{les échanges de deux chiffres d'écart } 5 $$

    Ce sont les échanges de \(0\) et \(5\), de \(1\) et \(6\), de \(2\) et \(7\), de \(3\) et \(8\), de \(4\) et \(9\) : c'est le cas de \(7\) et \(2\) dans \(47258\). En revanche, \(74258\) donne \(s = 44\) : l'échange de \(4\) et \(7\) est repéré.

La manche de tennis de table

Au tennis de table, une manche est gagnée par le premier joueur qui a au moins \(11\) points et au moins \(2\) points d'avance. Sur la tablette qui affiche le score, a est le score d'Alice et b celui de Bruno. Tom a écrit ce programme.

Tableau d'affichage d'une manche de tennis de table : Alice a 11 points, noté a, et Bruno 10 points, noté b Alice
\(11\)
\(a\)
Bruno
\(10\)
\(b\)
if a >= 11 or a - b >= 2:
    print("Alice gagne la manche")
elif b >= 11 or b - a >= 2:
    print("Bruno gagne la manche")
else:
    print("manche en cours")
  1. Dérouler le programme de Tom pour les scores \(11\) à \(7\), \(11\) à \(10\) et \(6\) à \(3\), Alice d'abord. Dans quels cas se trompe-t-il ?

    Avec or, une seule des deux conditions vraie suffit. Les trois fois, la première ligne est vraie : a >= 11 pour \(11\) à \(7\) et \(11\) à \(10\), a - b >= 2 pour \(6\) à \(3\).

    $$ \text{les trois fois : « Alice gagne la manche »} $$

    C'est faux pour \(11\) à \(10\), où Alice n'a qu'un point d'avance, et pour \(6\) à \(3\), où elle n'a pas \(11\) points : la manche continue.

  2. Corriger le programme de Tom en ne changeant que deux mots.

    Les deux conditions doivent être vraies en même temps : or devient and.

    if a >= 11 and a - b >= 2:
        print("Alice gagne la manche")
    elif b >= 11 and b - a >= 2:
        print("Bruno gagne la manche")
    else:
        print("manche en cours")

    Vérification : pour \(11\) à \(10\) et pour \(6\) à \(3\), les deux conditions composées sont fausses, et le programme affiche « manche en cours ».

  3. Prouver que, dans le programme corrigé, les conditions du if et du elif ne peuvent jamais être vraies en même temps. Peut-on alors échanger ces deux cas sans rien changer à l'affichage ?

    Si les deux étaient vraies, on aurait à la fois \(a - b \geqslant 2\) et \(b - a \geqslant 2\). En additionnant :

    $$ (a - b) + (b - a) \geqslant 4 \hspace{2em} \text{mais} \hspace{2em} (a - b) + (b - a) = 0 $$

    C'est impossible : au plus une des deux conditions est vraie, et l'ordre dans lequel on les teste ne change rien.

    $$ \text{oui, l'affichage reste le même} $$
  4. Une manche gagnée par Alice peut-elle finir sur le score \(13\) à \(9\) ? sur le score \(12\) à \(10\) ? Écrire la condition qui dit si a et b forment le score final d'une manche gagnée par Alice, et prouver qu'elle convient. On pensera au score juste avant le dernier point, marqué par Alice.

    Avant \(13\) à \(9\), le score était \(12\) à \(9\) : Alice avait déjà \(11\) points et \(3\) points d'avance, la manche était finie. Avant \(12\) à \(10\), il était \(11\) à \(10\) : la manche continuait.

    $$ 13 \ \text{à} \ 9 : \text{impossible} \hspace{2em} \text{et} \hspace{2em} 12 \ \text{à} \ 10 : \text{possible} $$

    Ce que doit vérifier un score final : la manche est gagnée, donc \(a \geqslant 11\) et \(a - b \geqslant 2\) ; et elle ne l'était pas à \(a - 1\) contre \(b\), donc \(a - 1 < 11\) ou \(a - 1 - b < 2\), c'est-à-dire \(a \leqslant 11\) ou \(a - b \leqslant 2\).

    Si \(a = 11\), l'avance d'au moins \(2\) points donne \(b \leqslant 9\). Si \(a \geqslant 12\), il faut \(a - b \leqslant 2\), et avec \(a - b \geqslant 2\), \(a - b = 2\).

    (a == 11 and b <= 9) or (a >= 12 and a - b == 2)
    $$ (a = 11 \quad \text{et} \quad b \leqslant 9) \quad \text{ou} \quad (a \geqslant 12 \quad \text{et} \quad a - b = 2) $$

    Chacun de ces scores est bien atteint : pour \(11\) à \(9\) ou moins, Bruno marque ses points, puis Alice les siens ; pour \(12\) à \(10\) ou plus, les joueurs marquent chacun leur tour jusqu'à l'égalité, puis Alice marque deux points de suite. Avant le dernier point, personne n'a jamais gagné.