Exercices



Enquêter sur une erreur dans un calcul de $\pi$

La somme des inverses des carrés des nombres entiers converge vers $\dfrac{\pi^2}{6}$.

$$\sum_{k=1}^{\infty} \dfrac{1}{k^2} = \dfrac{\pi^2}{6}$$

On utilise cette formule pour trouver une approximation de $\pi$.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import math

def terme(k: int) -> float:
    return 1 / (k**2)

def approxpi(n: int) -> float:
    s = 0
    # utilise les termes jusqu’à 1/n**2 inclus
    for k in range(n):
        s = s + terme(k)
    return math.sqrt(s * 6)

  1. Lors de l’exécution de approxpi(1000) une exception est levée à l’exécution de la ligne 4. Quel est le type d’exception ?

    a. ValueError
    b. SyntaxError
    c. TypeError d. ZeroDivisionError
    e. IndexError f. NameError

  2. Bien que l’exécution soit interrompue ligne 4, la source de l’erreur est ailleurs
    Expliquer d’où provient l’erreur et proposer un correctif.

Enquêter sur une erreur de calcul de produit scalaire

Le produit scalaire de deux vecteurs $\vec{u}$ et $\vec{v}$ de l’espace, de coordonnées respectives $(u_1, u_2, u_3)$ et $(v_1, v_2, v_3)$, est le nombre réel $u_1 v_1 + u_2 v_2 + u_3 v_3$.
Le code suivant permet de calculer des produits scalaires. Il est testé sur des vecteurs de l’espace choisis au hasard :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import random
from typing import Tuple

def scalaire(v1: Tuple[int], v2: Tuple[int]) -> float:  
    """
    Calcule le produit scalaire des deux vecteurs v1 et v2.
    """
    s = 0
    for k in range(len(v1)):
        s = s + v1[k] * v2[k]
    return s

def random_vect(n: int) -> tuple:
    """
    Génère un vecteur de taille n contenant des entiers non nuls
    entre -10 et 10
    """
    v = []
    for i in range(n):
        val = random.randint(-10, 10)
        if val != 0:
            v.append(val)
    return tuple(v)

def main():
    for i in range(20):
        print(scalaire(random_vect(3), random_vect(3)))

main()

Prises séparément, les deux fonctions semblent opérationnelles :

1
2
3
4
>>> scalaire((1, 2, 1), (-1, 3, -2))
3
>>> random_vect(3)
(-6, 2, 8)

Pourtant, lorsqu’on exécute la fonction main(), le programme n’affiche que quelques produits scalaires (ici 12, 118 et −104) puis lève une exception :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> main()
12
118
-104
Traceback (most recent call last): 
  File "scal.py", line 28, in <module> 
    main() 
  File "scal.py", line 26, in main 
    print(scalaire(random_vect(3), random_vect(3))) 
  File "scal.py", line 9, in scalaire 
    s = s + v1[k] * v2[k]
IndexError: tuple index out of range
  1. En analysant le traceback donné plus haut, indiquer quel est le type d’exception qui a été levée, et sur quelle ligne de code.

  2. Sur la ligne en question, qu’est-ce qui pourrait avoir provoqué l’erreur ?

  3. Proposer d’ajouter un affichage print(...) à un endroit du programme pour essayer de mettre en évidence la nature du problème, puis exécuter à nouveau le code.

  4. Expliquer l’erreur. Pourquoi ne se produit-elle pas toujours au même moment ?

  5. Proposer un correctif.

Calculer la valeur d’un mot : arithmancie

On souhaite associer une valeur numérique à une chaîne de caractères. La valeur associée à la chaîne est la somme des valeurs associées à chacun des caractères qui la composent. Aux lettres non accentuées de A à Z, qu’elles soient en minuscules ou en majuscules, on associe leur numéro d’ordre dans l’alphabet, de 1 à 26. À tous les autres symboles (lettres accentuées, chiffres, espaces, ponctuations), on associe la valeur 0.
On rappelle que ord("a") donne le code numérique associé à la lettre "a" passée en paramètre. La chaîne string.ascii_uppercase est définie dans le module string et vaut "ABCDEFGHIJKLMNOPQRSTUVWXYZ".

  1. Pour chacune des propositions suivantes, proposer un test qui montre que la fonction ne répond pas au cahier des charges de l’énoncé.

Proposition 1

1
2
3
4
5
6
def valeur1(chaine):
    s = 0 
    for c in chaine: 
        if c >= "A" and c <= "Z": 
            s = s + ord(c) - ord('A') + 1 
    return s

Proposition 2

1
2
3
4
5
6
def valeur2(chaine) : 
    s = 0 
    for c in chaine.upper(): 
        if c >= "A" and c < "Z": 
            s = s + ord(c) - ord('A') + 1 
    return s

Proposition 3

1
2
3
4
5
6
7
import string
def valeur3(chaine): 
    s = 0 
    for c in chaine.upper(): 
        if c in string.ascii_uppercase: 
            s = s + string.ascii_uppercase.index(c) 
    return s
  1. Proposer un ensemble complet de tests pour la fonction que l’on veut créer (on nomme cette fonction valeur).

  2. Proposer le code de cette fonction (il est possible de s’inspirer d’une des versions vues à la question 1. en la corrigeant). Ne pas oublier de vérifier que la fonction écrite passe bien les tests de la question 2.

Module d’arithmétique

Créer un module contenu dans un fichier nommé arithmetique.py. Ce module contient des fonctions sur l’arithmétique des nombres entiers. En particulier :

expo qui prend en paramètres les entiers $a$, $b$ et $n$ et calcule le reste de la division par $n$ de $a^b$ ;

pgcd qui prend en paramètres les entiers $a$ et $b$ et renvoie le plus grand diviseur commun de $a$ et $b$ (définition du pgcd) ;

decomposition qui prend un entier positif $n$ en paramètre et renvoie la liste de ses facteurs premiers ainsi que leur multiplicité.

  1. Créer le module avec les trois fonctions proposées.
  2. Proposer des dosctrings pour le module et les trois fonctions décrites. Annoter les fonctions avec les annotations de type.
  3. Ajouter quelques tests sous forme d’assertions.

Algorithme naïf pour la fonction decomposition

La première idée consiste à balayer la liste des nombres premiers en testant si le nombre premier $p$ divise $n$. Si oui, on recommence l’algorithme pour $n//p$, en ne testant que les diviseurs premiers encore envisageables. On s’arrête quand le nombre premier à tester devient supérieur à la racine carrée du nombre qu’il est censé diviser.
Ainsi pour décomposer 2088 en produit de facteurs premiers

2088 2 2 divise 2088 le quotient est 1044
1044 2 2 divise 1044, le quotient est 522
522 2 2 divise 522, le quotient est 261
261 3 2 ne divise pas 261, mais 3 divise 261 et le quotient est 87
87 3 3 divise 87 et le quotient est 29
29 ni 3, ni 5 ne divisent 29 et $7^2$ est plus grand que 29 (fin)

On obtient la décomposition attendue : $2088=2^3 \times 3^2 \times 29$.

Wikipedia

Suggestions de lecture :