Cette annexe s'adresse à qui n'a jamais écrit de Python, ou l'a oublié. Tous les chapitres de ce cours écrivent leurs méthodes dans ce langage et vous demandent de les compléter dans l'éditeur de la page; ils supposent la syntaxe connue et ne l'enseignent pas. Les pages qui suivent rassemblent le sous-ensemble exact dont le cours se sert — une quinzaine de constructions — avec, pour chacune, le piège qui coûte le plus de temps à un débutant en calcul numérique.
Ce n'est pas un cours de programmation. Si vous partez de zéro, les six premiers chapitres d'Introduction à la programmation («Premiers programmes» à «Listes et tuples») le sont, et son annexe A, Mémento Python, est la référence de syntaxe à garder ouverte. Ici, on va directement à ce qu'une méthode numérique demande: des nombres flottants, des listes qui servent de vecteurs et de matrices, des boucles indexées, des fonctions qui reçoivent d'autres fonctions, et un affichage qui donne le bon nombre de chiffres.
Le cours n'utilise que la bibliothèque standard: math et, à l'occasion, fractions. Ni numpy ni scipy: ils ne sont pas disponibles dans l'éditeur de la page, et ils cacheraient précisément les boucles dont parle ce cours.
Objectifs
À la fin de cette annexe, vous serez capable de:
- distinguer un entier
intd'un flottantfloat, prévoir le type du résultat d'une division, et écrire un nombre en notation scientifique; - représenter un vecteur par une liste et une matrice par une liste de lignes, indexer à partir de 0, copier une liste au lieu de l'aliaser, et éviter le piège de
[[0.0] * n] * n; - écrire une boucle
forsurrangedans les deux sens, une bouclewhileplafonnée par un nombre maximal d'itérations, et un testif; - définir une fonction avec des paramètres par défaut, lui passer une autre fonction (souvent une
lambda), et renvoyer plusieurs valeurs; - afficher un résultat avec un nombre choisi de chiffres, et lire une trace d'erreur jusqu'à la ligne fautive.
Nombres: entiers et flottants
Python a deux types de nombres réels pour ce cours. Un entier (int) s'écrit sans point, 3 ou -12, et il est exact et sans limite de taille: 10**20 + 1 vaut exactement 100000000000000000001. Un flottant (float) s'écrit avec un point ou un exposant, 3.0, 0.1, 1e-8, et c'est un nombre en double précision au sens du chapitre 1: 53 bits de mantisse, environ seize chiffres significatifs, et un arrondi à chaque opération. 1e20 + 1 vaut 1e+20: le 1 est perdu, exactement comme le chapitre 1 l'annonce.
Les opérateurs sont +, -, *, / et ** pour la puissance (2 ** 10 vaut 1024, 2 ** 0.5 vaut 1.4142135623730951). Trois d'entre eux surprennent:
/donne toujours un flottant, même entre entiers:7 / 2vaut3.5;//est la division entière, arrondie vers le bas:7 // 2vaut3, mais-7 // 2vaut-4;%est le reste:7 % 2vaut1.
Les fonctions mathématiques sont dans le module math, que l'on importe en tête de programme par import math puis qu'on appelle par math.sqrt(2), math.exp(x), math.cos(x), ou que l'on importe nom par nom: from math import sqrt, pi. Retenez que math.log est le logarithme naturel — math.log(100) vaut — et que s'écrit math.log10. Les fonctions abs, max, min et sum sont, elles, toujours disponibles sans import.
Listes: vecteurs et matrices
Un vecteur est une liste de flottants: b = [8.0, -11.0, -3.0]. Sa longueur est len(b), et ses composantes sont b[0], b[1], b[2]: les indices commencent à 0. C'est la convention de tous les programmes du cours, et c'est pourquoi un indice allant de à dans une formule devient i allant de 0 à n - 1 dans le code. L'indice -1 désigne le dernier élément: b[-1] vaut -3.0.
Une matrice est une liste de lignes: A = [[2.0, 1.0, -1.0], [-3.0, -1.0, 2.0], [-2.0, 1.0, 2.0]], et le coefficient s'écrit A[i][j] — ligne d'abord, colonne ensuite. C'est la matrice témoin du cours, celle des chapitres 3 à 5.
Pour fabriquer une liste, trois écritures suffisent:
[0.0] * ncrée un vecteur nul de longueurn;[f(x) for x in xs]crée la liste des images, c'est une compréhension de liste; on peut y ajouter un filtre,[x for x in xs if x > 0];liste.append(valeur)ajoute un élément à la fin.
Une tranche extrait une sous-liste: b[1:3] contient b[1] et b[2], sans b[3], et b[:] est une copie complète.
Boucles et tests
La boucle for parcourt une suite d'indices produite par range:
range(n)donne — sans ;range(1, n)donne ;range(n - 1, -1, -1)donne , à rebours, comme dans une remontée triangulaire (chapitre 3): le deuxième argument est exclu, d'où le pour atteindre .
Le corps de la boucle est indenté de quatre espaces; c'est l'indentation, et non une accolade, qui dit où il s'arrête. On peut aussi parcourir directement les éléments, for ligne in A:, quand l'indice ne sert pas.
La boucle while condition: répète tant que la condition est vraie. En calcul numérique, une boucle qui attend la convergence doit toujours être plafonnée: une méthode qui diverge ne remplira jamais sa condition d'arrêt. C'est pourquoi toutes les méthodes itératives du cours s'écrivent for k in range(kmax): avec un return dès que le critère est satisfait, plutôt que while erreur > tol:. Dans l'éditeur de la page, un programme qui tourne plus de dix secondes est d'ailleurs interrompu.
Un test s'écrit if, éventuellement suivi de elif et else; les comparaisons sont <, <=, ==, !=, et se combinent par and, or, not. L'instruction break sort de la boucle courante.
Fonctions
Une fonction se définit par def, reçoit des paramètres et renvoie un résultat par return. Trois usages reviennent à chaque chapitre.
Les paramètres par défaut. def bissection(f, a, b, tol=1e-10): permet d'appeler bissection(f, 1, 2) sans préciser la tolérance, ou bissection(f, 1, 2, tol=1e-6) pour la changer.
Les fonctions en argument. Une méthode numérique s'applique à une fonction que l'on ne connaît pas d'avance: la bissection reçoit , Euler reçoit le second membre . En Python, une fonction est une valeur comme une autre et se passe en argument. Pour une fonction d'une ligne, on l'écrit sur place avec lambda: lambda x: x**3 - x - 1 est la fonction , l'équation témoin du cours.
Plusieurs résultats. return x, k renvoie un couple (un tuple), que l'appelant déballe par racine, k = newton(...).
Une chaîne entre triples guillemets placée juste sous le def est la documentation de la fonction; les programmes du cours s'en servent pour dire ce que contient chaque argument.
Afficher le bon nombre de chiffres
print(x) affiche un flottant avec le nombre de chiffres nécessaire pour le retrouver exactement, souvent dix-sept. Pour un tableau d'itérés, on choisit le format. Le cours utilise l'opérateur %, dont trois codes suffisent:
"%d" % kpour un entier;"%.10f" % xpour un flottant à dix décimales;"%.4e" % errpour une notation scientifique à quatre décimales,1.2951e-02.
Plusieurs valeurs se passent en tuple: print("%d %.15f %.2e" % (k, x, err)). Les f-strings font la même chose, print(f"{k} {x:.15f} {err:.2e}"); les deux écritures sont équivalentes et vous pouvez employer celle que vous préférez dans vos réponses. Rappelez-vous la règle du cours: afficher les chiffres que l'on a calculés, pas davantage.
Lire une trace d'erreur
Quand un programme s'arrête sur une erreur, Python affiche une trace (traceback): la liste des appels en cours, du plus ancien au plus récent, puis le type de l'erreur. Lisez-la de bas en haut. La dernière ligne donne le type et le message; celle du dessus donne le fichier, le numéro de ligne et le texte de l'instruction fautive. Dans l'éditeur de la page, le nom de fichier est celui de l'exercice, newton.py par exemple.
Quatre erreurs couvrent l'essentiel de ce que vous rencontrerez:
IndexError: list index out of range— un indice hors de la liste, presque toujours unrangedécalé d'une unité ou une liste plus courte que prévu;ZeroDivisionError— un pivot nul (chapitre 3), une dérivée nulle dans Newton (chapitre 2), un pas ;NameError: name 'x' is not defined— une faute de frappe, ou une variable utilisée hors de la fonction qui la définit;TypeError— une opération entre types incompatibles, souvent une liste là où l'on attendait un nombre.
Une méthode qui diverge ne lève en général aucune erreur: elle produit inf ou nan, ou un OverflowError sur **. L'annexe B d'Introduction à la programmation, «Erreurs fréquentes et comment les lire», détaille la méthode de recherche.
On veut parcourir les indices d'une liste de longueur , à rebours, comme dans une remontée triangulaire. Quelle écriture est juste?
Écrivez produit(A, v), qui renvoie le vecteur pour une matrice donnée comme liste de lignes, et norme_inf(v), qui renvoie . Tel quel, le squelette s'arrête sur une IndexError: lisez la trace pour voir où, puis complétez. Le programme affiche pour la solution exacte du système témoin, puis la norme du résidu d'une solution approchée.
Synthèse
- Un
intest exact et illimité, unfloatest un double IEEE avec un arrondi par opération;/donne toujours un flottant,math.logest , et deux flottants calculés ne se comparent jamais avec==. - Un vecteur est une liste, une matrice une liste de lignes,
A[i][j]est , et les indices commencent à 0.c = bcrée un alias,b[:]une copie; une matrice se crée par[[0.0] * n for _ in range(n)], jamais par[[0.0] * n] * n. range(n)s'arrête à ;range(n - 1, -1, -1)descend jusqu'à 0. Une boucle qui attend la convergence est toujours plafonnée par unkmax.- Une fonction se passe en argument, souvent sous forme de
lambda;return x, krenvoie deux valeurs; les paramètres par défaut portent les tolérances. - On affiche avec
%d,%.10f,%.4e, et l'on n'affiche que les chiffres calculés. Une trace d'erreur se lit de bas en haut: le type, puis la ligne.
Exercices
Vous pouvez afficher le corrigé directement sous chaque énoncé après avoir cherché la solution.
Sans l'exécuter, dites ce qu'affiche chacune des lignes suivantes, puis vérifiez dans un éditeur.
print(7 / 2, 7 // 2, -7 // 2, 7 % 2)print(list(range(1, 5)), list(range(4, -1, -1)))print(math.log(100), math.log10(100)), aprèsimport mathv = [1.0, 2.0],w = v,w.append(3.0),print(len(v))
Solution
3.5 3 -4 1. La division/donne un flottant;//arrondit vers le bas, d'où-4et non-3pour .[1, 2, 3, 4] [4, 3, 2, 1, 0]. La borne de droite est exclue dans les deux cas.4.605170185988092 2.0.math.logest le logarithme naturel: .
Écrivez une fonction bissection(f, a, b, tol=1e-6) qui suppose et de signes contraires et renvoie le milieu du dernier intervalle ainsi que le nombre d'itérations, en s'arrêtant dès que la longueur de l'intervalle est inférieure ou égale à 2 * tol. Appliquez-la à sur . Combien d'itérations fait-elle, et pourquoi la boucle n'a-t-elle pas besoin d'un ici?
Références
- Introduction à la programmation (ce site), chapitres 1 à 6, annexe A «Mémento Python» et annexe B «Erreurs fréquentes et comment les lire».
- Documentation officielle de Python 3, Le tutoriel Python, sections 3 à 5 (nombres, listes, contrôle du flux, fonctions), docs.python.org/fr/3/tutorial.
- Documentation officielle de Python 3, module
mathet section «Arithmétique en nombres à virgule flottante: problèmes et limites» du tutoriel. - Higham, N. J., Accuracy and Stability of Numerical Algorithms, SIAM, Philadelphie, chapitre 1 (pour le flottant derrière le type
float).