On considère un ensemble de voitures sur une file à sens unique. Une file ##L## de longueur ##n## est représentée par ##n## cases. Une case peut contenir au plus une voiture. Lorsqu’une case d’indice ##i## de la file est occupée par une voiture, ##L[i]## vaut ##True##, sinon ##L[i]## vaut ##False##.
Afin de comparer plus efficacement les files représentées par des listes de booléens, on remarque que ces listes représentent un codage binaire où ##True## correspond à ##1## et ##False## à ##0##.

a) Soit L une liste représentant une file de longueur ##n## et ##i## un entier tel que ##0 \leq i \leq n##.
Définir en Python la fonction ##occupe(L, i)## qui renvoie ##True## lorsque la case d’indice ##i## de la file est occupée par une voiture et ##False## sinon.

b) Combien existe-t-il de files différentes de longueur ##n## ? Justifier votre réponse

c) Écrire la fonction ##versEntier(L)## prenant une liste de booléens en paramètre et renvoyant l’entier correspondant.
Par exemple, l’appel ##versEntier([True, False, False])## renverra ##4##.

d) On veut écrire la fonction inverse de ##versEntier##, transformant un entier en une liste de booléens.
Que doit être au minimum la valeur de ##taille## pour que le codage obtenu soit satisfaisant ? On suppose que la valeur de ##taille## est suffisante. Quelle condition booléenne faut-il écrire en quatrième ligne du code ci-dessous ?

Python
def versFile(n, taille):
    res = [False] * taille
    i = taille - 1
    while ....:
        if n % 2 != 0:              # % est le reste de la division euclidienne de n par 2
            res[i] = True
        n = n // 2                  # // est le quotient de la division euclidienne de n par 2
        i -= 1
    return res

Cet exercice est inspiré du sujet de la filière MP, Mines Ponts 2017. L’énoncé du sujet est accessible en cliquant-ici, le corrigé associé en cliquant-ici.

Besoin d’aide ?

a) La case d’indice ##i## contient la valeur à retourner.

b) Définir une boucle sur la liste en pensant à la notation binaire.

c) Envisager une exécution pas à pas du programme pour mieux l’analyser, en gardant en mémoire la question précédente.

d) Envisager une exécution pas à pas du programme pour mieux l’analyser, en gardant en mémoire la question précédente.

Correction détaillée

a) Comme la file est représentée par une liste, on peut accéder directement à l’état d’une position en utilisant son indice, sans avoir besoin de parcourir toute la liste, ce qui nous donne :

Python
def occupe(L, i):
    """
    Fonction qui vérifie si la position i dans la liste L est occupée.
    :param L: Liste représentant la liste de positions
    :param i: Position à vérifier
    :return: True si la position est occupée, False sinon
    """
    return L[i]

b) Prenons l’exemple d’une file de longueur ##3##.
Pour la case d’indice ##0##, on a deux possibilités, soit elle est occupée (##True##) soit elle est vide (##False##).
Pour la case d’indice ##1##, on a également deux possibilités, occupée ou vide
Pour la case d’indice ##2##, on a également deux possibilités, occupée ou vide

Ainsi, pour chaque case, il y a 2 choix possibles.
Le nombre total de combinaisons est donc :

$$2 \times 2 \times 2 = 2^3 = 8$$

Pour une file de longueur ##n##, chaque position peut prendre 2 états : occupée (##True##) ou vide (##False##).
Par conséquent, le nombre total de files possibles est ##2^n## pour une longueur de ##n##.

c) On considère l’exemple suivante ##L = [True, True, False, True]##
En convertissant en binaire on obtiens : ##1101_b = 1\times 2^3 + 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 = 8 + 4 + 0 + 1 = 13##

Nous allons utiliser deux variables ##result## et ##puissance##. Voici les étapes importantes :
– À chaque étape de la boucle ##for## on ajoute à ##result## la valeur de puissance si la valeur binaire est ##1##
– À chaque étape de la boucle ##for## on multiplie par ##2## la variable ##puissance##

Python
def versEntier(L):
    """
     Convertit une séquence de bits en entier.
    :param L: Chaîne de caractères composée de '0' et '1' représentant un nombre binaire (ex: '1101')
    :return: Entier correspondant
    """
    result = 0
    puissance = 1
    for bit in reversed(L):
        result = result + int(bit) * puissance
        puissance = puissance * 2
    return result

assert versEntier("1101") ==  13
assert versEntier("101010") ==  42
assert versEntier("11111111") == 255
assert versEntier("0") == 0
assert versEntier("1") == 1

print("Tous les tests ont réussi !")

d) Pour un codage sur ##3## bits, la valeur maximale est ##111_b = 4 + 2 + 1 = 7 = 2^3 – 1##. Cela correspond à ##L = [True, True, True]##.
Pour un codage sur ##x## bits, la valeur maximale vaut ##2^x -1##. On doit donc avoir : ##n \leq 2^{taille} – 1##

On considère l’exemple suivant : ##taille = 4## et ##L = [True, True, True, True]##.
On a ##1101_b = 8 + 4 + 0 + 1 = 13##.

  • Première étape de la boucle ##while : i = 3##. Le reste vaut ##13%2 = 1##. Le quotient vaut ##13//2 = 6##.

  • Deuxième étape de la boucle ##while : i = 2##. Le reste vaut ##6%2 = 0##. Le quotient vaut ##6//2 = 3##.

  • Deuxième étape de la boucle ##while : i = 1##. Le reste vaut ##3%2 = 1##. Le quotient vaut ##3//2 = 1##.

  • Quatrième étape de la boucle ##while : i = 0##. Le reste vaut ##3%2 = 1##. Le quotient vaut ##3//2 = 0##.

La condition booléenne à écrire est : ##while\: i\geq0##. On peut aussi écrire la condition ##while\: n>0##.