Algorithmique et programmation en secondeRenée De Graeve Bernard Parisse |
Table des matières
- Chapitre 1 Avant-propos
- Chapitre 2 Types, fonctions
- 2.1 Types
- 2.2 Les fonctions
- 2.2.1 Quelques fonctions algébriques de Xcas
- 2.2.2 Quelques fonctions aléatoires de Xcas
- 2.2.3 Définition d’une fonction algébrique d’une variable
- 2.2.4 Définition d’une fonction algébrique de 2 variables
- 2.2.5 Définition d’une fonction algébrique sans lui donner un nom
- 2.2.6 Définition d’une fonction algébrique par morceaux avec quand
- Chapitre 3 Les instructions de programmation utilisées sur des exemples
- 3.1 Commentaires et documentation.
- 3.2 Stocker une valeur dans une variable avec :=
- 3.3 Enlever une valeur stockée dans une variable avec purge
- 3.4 Suite d’instructions avec ; ou :;
- 3.5 L’instruction retourne ou return
- 3.6 L’instruction local
- 3.7 L’instruction pour
- 3.8 L’instruction pour avec un pas
- 3.9 L’instruction si
- 3.10 Utiliser une fonction utilisateur dans un programme
- 3.11 L’instruction tantque
- 3.12 Interruption d’une boucle
- 3.13 Exemple 9 : autre exemple de boucle tantque
- 3.14 Exemple 10 : encore un autre exemple de boucle tantque
- 3.15 Exercice : Algorithme de tracé de courbe
- 3.16 Mettre au point un programme
- Chapitre 4 Résolution d’équations
- Chapitre 5 Les figures en géométrie plane avec Xcas
- 5.1 Le point : point et le segment : segment
- 5.2 Les coordonnées d’un point : coordonnees
- 5.3 La droite et son équation : droite et equation
- 5.4 Ligne brisée : polygone_ouvert
- 5.5 Les polygones : triangle, carre, polygone
- 5.6 Le cercle et son équation : cercle et equation
- 5.7 Les tangentes à un cercle passant par un point et leurs équations
- 5.8 Exercice : les lunules d’Hippocrate
- Chapitre 6 La géométrie analytique
- 6.1 Les segments
- 6.2 Les droites
- 6.3 Triangles et quadrilatères définis par les coordonnées des sommets
- 6.4 Les vecteurs
- 6.5 Changement de repères
- 6.6 Cercles, Tangentes à un cercle
- 6.6.1 Équation d’un cercle défini par son centre et son rayon
- 6.6.2 Équation d’un cercle défini par son diamètre
- 6.6.3 Équation d’un cercle défini par son centre et son rayon ou par son diamètre
- 6.6.4 Centre et rayon d’un cercle donné par son équation
- 6.6.5 Construire la tangente à un cercle en l’un de ses points
- 6.6.6 Construire les tangentes à un cercle passant par un point
- 6.6.7 Solution analytique des tangentes à un cercle
- Chapitre 7 Quelques tests géométriques
- Chapitre 8 Statistiques
- Chapitre 9 Les algorithmes du document ressource Python d’eduscol
- Chapitre 10 Aide
- Annexe A Xcas, Python et Javascript.
- Annexe B Les biais des langages interprétés
Index
|
|
Chapitre 1 Avant-propos
Ce document est principalement destiné aux enseignants qui souhaitent utiliser Xcas pour enseigner l’algorithmique au lycée. Nous espérons qu’il sera aussi consulté par des élèves. Dans sa version HTML consultable depuis un navigateur, certains champs de saisies peuvent être modifiés et testés directement, y compris sur une tablette ou un smartphone, ce qui devrait être un plus par rapport à un cours de programmation papier ou PDF (les fonctions “utilitaires” qui sont appelées plusieurs fois par d’autres fonctions n’ont pas besoin d’être validées par l’utilisateur, elles sont interprétées au chargement).
L’utilisation de Xcas peut se faire depuis un terminal mobile (smartphone
ou tablette) sans installation, il suffit d’ouvrir un navigateur (Firefox
recommandé) et de suivre ce lien :
http://www-fourier.ujf-grenoble.fr/~parisse/xcasfr.html
(L’accès réseau est nécessaire uniquement lors de la première
consultation).
Il n’est donc pas indispensable d’aller en salle informatique
pour faire un exercice d’algorithmique pendant un cours de mathématiques,
on peut utiliser les smartphones (en mode avion)
ou les tablettes des élèves comme des super-calculatrices (formelles,
graphiques, 3d, ... il ne manque que le mode examen...). C’est une
raison supplémentaire pour écrire ce document, car faire programmer
les algorithmes par les élèves nous parait indispensable
pour les motiver par la satisfaction de faire tourner son
programme puis de le modifier et de l’améliorer.
On peut d’ailleurs regretter le nombre important d’exercices de baccalauréat qui demandent de comprendre ce que fait un algorithme non commenté. Dans ce type d’exercices, l’élève n’a aucun rôle créateur (il ne conçoit pas l’algorithme), il a un rôle analogue à celui d’un professeur corrigeant des copies ce qui n’est pas du tout motivant1, et a devant les yeux un modèle désastreux d’algorithme puisque non commenté !
De plus, comprendre ce que fait un algorithme non commenté est souvent plus difficile que d’en concevoir un, car les notations de variables, par exemple, ne prennent souvent sens qu’une fois l’algorithme compris. Heureusement, d’autres exercices proposent des maquettes d’algorithmes à compléter ce qui est bien plus formateur. Actuellement aucun langage n’est imposé en 2nde, 1ère et Terminale, nous espérons que cette situation va perdurer et que beaucoup d’enseignants résisteront aux pressions de certains de vouloir imposer un langage unique (comme pour les enseignements obligatoires d’informatique en classe préparatoires). Ainsi on peut configurer Xcas pour programmer avec des mots-clefs en français ou avec un style compatible avec Python, Maple, Mupad, ti89/92 (les personnes connaissant Python peuvent consulter l’appendice A.1 qui décrit plus en détails les différences entre Python et Xcas).
La plupart des langages interprétés permettent d’apprendre à programmer les concepts algorithmiques au programme du lycée (test, boucle, variable, affectation, fonction). En effet, pour les élèves, la difficulté principale ce sont les concepts algorithmiques, rarement la syntaxe du langage lui-même, car ils peuvent se faire aider par l’enseignant s’ils sont bloqués. C’est donc aux enseignants qu’il revient de choisir un langage avec lequel ils sont à l’aise, non seulement pour écrire eux-mêmes un programme, mais aussi pour trouver rapidement une erreur de syntaxe ou d’exécution dans un programme d’un de leurs élèves.
Pour la grande majorité des élèves, il est probablement souhaitable qu’ils soient confrontés lors des changements de professeur à plusieurs langages au cours de leur scolarité (par exemple Xcas, calculatrices, Python, Javascript ...), ce qui leur permettra de mieux comprendre les concepts universels partagés (l’algorithmique) et les biais et particularités propres à un langage donné (voir en appendice), et facilitera aussi leur adaptation à d’autres langages. Pour ceux qui se destinent à des études scientifiques, il nous parait important qu’ils soient aussi confrontés à d’autres types de langages (compilés, fonctionnels ...) au cours de leurs études dont au moins un langage de plus bas niveau : les langages interprétés permettent d’utiliser facilement des types ou instructions puissantes, se confronter avec un langage de plus bas niveau permet de mieux comprendre ce qui est basique ou ne l’est pas et ce qui est intrinsèquement rapide ou/et couteux en ressource mémoire ou ne l’est pas (on peut voir ça comme l’analogue entre faire une démonstration ou admettre un théorème).
Xcas est fortement orienté mathématique et de ce fait peut facilement interagir avec les thèmes du programme de maths, tous les types mathématiques au programme du lycée sont directement accessibles (par exemple : entiers et rationnels, nombres approchés réels et complexes, vecteurs, polynômes et matrices). Nous avons adapté le langage en français pour en faciliter l’apprentissage d’après notre expérience d’enseignement avec des publics divers :
-
Les structures sont délimitées par des mots-clefs explicites
en français
(si . alors . sinon . fsi
),
(pour . de . jusque . faire ... fpour
),
(tantque . faire ... ftantque
)...
L’indentation sert à controler qu’on n’a pas fait de faute de syntaxe (non-fermeture d’une parenthèse par exemple). Les diverses interfaces de Xcas proposent des assistants pour créer facilement les structures de controle usuelles (fonction, test, boucle). - Il faut déclarer explicitement les variables locales, ainsi une faute de frappe dans un nom de variable est détectée et un avertissement est affiché.
- Lorsqu’on programme une fonction, on peut lui passer en argument des variables qui sont de type fonction ou expression, ceci facilite l’écriture de certains algorithmes (dichotomie, méthode des rectangles par exemple)
- Les thèmes d’algorithmique abordés au lycée sont presque toujours déjà implémentés dans une commande de Xcas, ceci permet de vérifier en comparant le résultat de la fonction qu’on vient de concevoir avec la commande interne.
- 1
- Imagine-t-on donner les étapes de calcul d’une démonstration, sans explications, et demander quel est le théorème qui a été démontré ?
Chapitre 2 Types, fonctions
2.1 Types
2.1.1 Les entiers, les rationnels et les nombres approchés.
- les entiers sont des nombres de , par exemple -2,
- les rationnels sont des nombres de , par exemple ,
- les nombres approchés sont des nombres décimaux1, par exemple .
Attention, le séparateur entre partie entière et partie fractionnaire est le point.
On peut utiliser la notation scientifique mantissee
exposant pour entrer des nombres approchés, par exemple1.2e-7
signifie1.2
multiplié par . - les nombres réels sont représentés par des nombres décimaux ou par des valeurs symboliques, par exemple .
Pour avoir une valeur approchée d’un nombre réel on utilise la commande :
evalf, par exemple evalf(sqrt(2)) ou evalf(sqrt(2),20) pour
avoir une valeur approchée de avec 20 chiffres significatifs.
2.1.2 Les listes, les séquences et les chaînes de caractères
Définition d’une liste
Qu’est-ce qu’une liste ?
C’est une énumération d’objets, dont l’ordre est important.
Cela peut servir à représenter les coordonnées d’un point ou
d’un vecteur, à contenir une liste de valeurs (observations)
en statistiques, ...
Une liste est délimitée par des crochets [] et les éléments de
la liste sont séparés par une virgule ,
Création d’une liste
-
range
range(n) renvoie la liste [0,1..n-1]
range(a,b) renvoie la liste [a,a+1..b[
range(a,b,p) la liste [a,a+p,a+2p..b[
- seq renvoie la liste obtenue lorsqu’on calcule
une expression dépendant d’un paramètre var
qui varie entre a
et b (on peut ajouter un argument optionnel, le
pas p, seq a 4 ou 5 arguments)
- makelist renvoie une liste faite à partir d’une fonction ou d’une
constante.
Définition d’une séquence
Qu’est-ce qu’une séquence ?
C’est presque la même chose qu’une liste, mais sans crochets, on ne peut
donc pas créer une séquence de séquences alors qu’on peut créer
une liste de listes. Par exemple les arguments d’une fonction sont
regroupés en une séquence.
Une séquence n’est pas délimitée
(ou est délimitée par des parenthèses ()) et les éléments
de la séquence sont séparés par une virgule ,
Création d’une séquence
-
seq renvoie la séquence obtenue lorsque var varie entre
a et b (avec un argument optionnel pas=p) (seq a 2 ou 3 arguments)
- $ est la version infixée de seq
Transformation d’une séquence en liste et vice-versa
Si S est une séquence alors [S] est une liste.
Si L est une liste alors op(L)] est une séquence.
Définition d’une chaine de caractères
Qu’est-ce qu’une chaine de caractères ?
C’est la concaténation de 0, 1 ou plusieurs caractères.
Une chaine de caractères est délimitée par ""
2.1.3 Les instructions sur les listes les séquences et les chaînes de caractères
-
dim(L) ou len(L)
renvoie le nombre d’éléments de la liste L.
dim(S) ou len(S) renvoie le nombre d’éléments de la séquence S.
dim(s) ou len(s) renvoie le nombre de caractères de la chaîne s. - [] représente la liste vide et dim([]) vaut 0.
NULL représente la séquence vide et dim(NULL) vaut 0.
"" représente la chaîne vide et dim("") vaut 0. - Les éléments de la liste sont numérotés de 0 jusque dim(L)-1.
L|0] désigne le premier élément de la liste et L[dim(L)-1] ou L[-1] désigne le dernier élément de la liste L.
Les éléments de la séquence sont numérotés de 0 jusque dim(S)-1.
S|0] désigne le premier élément de la séquence et S[dim(S)-1] ou S[-1] désigne le dernier élément de la séquence.
s|0] désigne le premier caractère de la chaîne et s[dim(s)-1] désigne le dernier caractère de la chaîne S.
- gauche(L,n) renvoie les n premiers éléments de la liste L
(c’est le côté gauche de la liste).
droit(L,n) renvoie les n derniers éléments de la liste L (c’est le côté droit de la liste).
gauche(s,n) renvoie les n premiers caractères de la chaîne s (c’est le côté gauche de la chaîne).
droit(s,n) renvoie les n derniers caractères de la chaîne s (c’est le côté droit de la chaîne).
Par exemple : soient la liste L:=[2,24,1,15,5,10] et la chaîne s:="Bonjour".
L1:=seq(2*k,k,0,4) crée la liste [0,2,4,6,8] count_eq, count_inf, count_sup, count
: nombre d’éléments égaux, inférieurs, supérieurs ou vérifiant une condition
count_sup(2,L1)
ou count(x->x>2,L1) compte le nombre d’éléments de L1 supérieurs à 2.
S1:=seq(2*k,k=0..4) crée la séquence (0,2,4,6,8)
count(x->x>2,S1) compte le nombre d’éléments de S1 supérieurs à 2.- sum(L) renvoie la somme des éléments de la liste L.
sum(S) renvoie la somme des éléments de la séquence S. - Quelques exemples :
- append
append(L,a) renvoie la liste où l’élément a à été rajouté à la fin de la liste L.
On tape :
Attention
Pour ajouter 4 à la fin de la liste L on tape :
ou
- Il est facile d’ajouter un élément à une séquence ou de concaténer 2
séquences.
Supposons que S soit la séquence (1,2,3)(S:=1,2,3) et que L soit la liste [1,2,3] (L:=[1,2,3]).
Pour modifier une séquence il suffit par exemple, décrire :
S:=S,4 (resp S:=S,4,5,6 ou S:=S,(4,5,6)) pour que S soit la séquence (1,2,3,4) (resp (1,2,3,4,5,6)
alors que pour modifier une liste il faut écrire :
L:=append(L,4) (resp L:=append(L,4,5,6) ou L:=concat(L,4,5,6) ou L:=concat(L,[4,5,6])) pour que L soit la liste [1,2,3,4] (resp [1,2,3,4,5,6]).
Mais attention !
L:=append(L,[4,5,6]) renvoie [1,2,3,[4,5,6]]
Pour les chaînes de caractères, supposons que s soit la chaîne "abc" (s:="abc"), on peut écrire pour lui rajouter "def" :
s:=append(s,"def") ou s:=concat(s,"def") ou s:=s+"def")
- prepend
prepend(L,a) renvoie la liste où l’élément a à été rajouté au début de la liste L.
On tape :
Attention
Pour ajouter 0 au début de la liste L on tape :
ou
- concat
concat(L1,L2) renvoie la concaténation des deux listes L1 et L2).
On tape :
Attention
Pour ajouter 4,5,6,7 à la fin de la liste L on tape :
ou
- rotate
rotate(L) (resp rotate(L,n)) renvoie la liste obtenue en mettant le dernier élément (resp la fin de la liste à partir du n-ième élément) en premier (par défaut n=-1).
On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
- shift
shift(L,n) : si n>0 on remplace les n premiers éléments de la liste par des 0 et on renvoie ces 0 à la fin de la liste,
si n<0 renvoie la liste obtenue en remplaçant les -n derniers éléments de la liste par 0 et on renvoie ces 0 au début de la liste, puis on renvoie la liste ainsi obtenue. (par défaut n=-1 ce qui signifie que le dernier élément de la liste est supprimé et on met un 0 au début de la liste). On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
- suppress
suppress(L,n) renvoie L sans son élément d’indice n.
On tape (attention les indices commencent à 0) :
Attention
Pour modifier le contenu de L, on tape :
ou
- reverse
reverse(L) ou revlist(L) renvoie la liste L inversée.
On tape :
Attention
Pour modifier le contenu de L, on tape :
ou
2.1.4 Les booléens
Définition
L’ensemble des booléens est un ensemble à 2 éléments :
vrai ou 1 et faux ou 0.
Pour faire des tests, on utilise des opérateurs booléens.
Opérateur booléen infixé qui teste l’égalité ==
Pour tester l’égalité entre deux objets (réels, entiers, expressions,
listes..) on utilise l’opérateur ==
. Le test renvoie vrai si les
deux objets sont égaux, au sens où leur représentation informatique
est identique. Pour tester que deux expressions littérales
contenues dans les variables a
et b
sont mathématiquement
équivalentes, il est parfois nécessaire de faire une commande explicite
de simplification, au lieu d’écrire a==b
on écrit
simplify(a-b)==0
.
Exemple :
Attention
-
Il ne faut pas confondre les différentes significations du signe
=
Le signe := sert à stocker une valeur dans une variable, le signe = sert à définir une équation (ou à donner la valeur par défaut d’un argument, cf. la section 2.2.4), le signe == sert à tester l’égalité. Xcas accepte toutefois = dans certaines situations non ambigües (affectation ou test d’égalité). - Xcas travaille par défaut avec des nombres exacts donc :
3+10^
(-16)==3 renverra faux ou false car il compare 2 nombres rationnels alors qu’avec un logiciel qui ne fait pas de calcul formel, il se peut que 3+10^
(-16)==3 renvoie True car il compare 2 nombres flottants i.e. approchés.
Opérateur booléen infixé qui teste la non égalité !=
Opérateur booléen infixé qui teste l’inégalité <, >, <=, >=
2.1.5 Expressions, polynômes
Simplification d’une expression avec normal
Xcas renvoie le résultat d’un calcul littéral sans le simplifier
(sauf pour les calculs dans les rationnels).
Il faut utiliser la fonction normal ou simplify
pour obtenir un résulat simplifié.
Les polynômes
Un polynôme à une indéterminée à coefficients dans est
déterminé par une
séquence d’éléments de , c’est l’expression :
(ou ).
est le degré du polynôme.
On dit que l’on a écrit le polynôme selon les puissances décroissantes
(ou croissantes).
sont les coefficients du polynôme et est la variable ou
l’indéterminée du polynôme.
On notera l’ensemble des polynômes à une indéterminée : .
Un polynôme à 2 indéterminées et à coefficients dans est déterminé par
une séquence d’éléments de
et a pour expression :
(ou )
Par exemple :
si
Le polynôme s’écrit :
Le degré par rapport à du polynôme de cet exemple et égal à 2.
Le degré par rapport à du polynôme de cet exemple et égal à 3.
Coefficients et degré d’un polynôme
Xcas représente les polynômes
soit sous la forme d’une expression symbolique
(par exemple x^
2+2x+3 où x est l’indéterminée).
soit comme la séquence des coefficients selon les puissances décroissantes
(poly1[1,2,3] i.e. [a2,a1,a0]:=[1,2,3]).
- Pour avoir le degré d’une expression polynômiale par rapport à une variable, on utilise l’instruction degree qui renvoie le degré d’un polynôme (on peut ajouter un 2ième argument si le polynôme est symbolique en une indéterminée qui n’est pas x).
- Pour avoir les coefficients d’un polynôme par rapport à une variable
on utilise l’instruction symb2poly qui renvoie la liste des coefficients
d’un polynôme par rapport au 2ième argument (x est la variable par
défaut). Ainsi si L:=symb2poly(P(x)),
degree(P(x)) est égal à
dim(L)-1.
La commande poly2symb effectue le calcul inverse, ainsi :
symb2poly(y^
2+2y+3) renvoie poly1[1,2,3].
poly2symb([1,2,3],y) renvoie (y+2)*y+3 - Pour avoir le coefficient de degré donné d’un polynôme par rapport à une variable, on utilise l’instruction coeff.
- Exemples :
2.1.6 Connaître les types et les sous-types
Les types
Xcas sait reconnaître le type d’un objet.
Pour avoir le type de l’objet a ou le contenu d’une variable a, on
utilise type(a).
Les types utilisés ici sont :
real ou 1 ce qui signifie que a contient un nombre flottant.
integer ou 2 ce qui signifie que a contient un nombre
entier,
rational ou 10 ce qui signifie que a contient un nombre
rationnel,
func ou 13 ce qui signifie que a est le nom d’une
fonction,
vecteur ou 7 ce qui signifie que a contient une liste,
string ou 12 ce qui signifie que a contient une
chaîne de caractères,
expression ou 8 ce qui signifie que a contient une
expression,
identifier ou 6 ce qui signifie que a contient le nom d’une
variable non affectée.
Les sous-types
Certains types de variables peuvent servir à plusieurs usages : par exemple
une liste peut représenter les coordonnées d’un point dans l’espace
ou les coefficients d’un polynôme ou un ensemble. Xcas possède
une commande subtype permettant de préciser le type d’une variable.
Pour avoir le sous-type de la variable a, on utilise subtype(a).
Par exemple si a contient une liste,
subtype(a) renvoie 1 pour une séquence, 2 pour un ensemble,
10 pour un polynôme et 0 sinon.
2.2 Les fonctions
On distingue les fonctions ou commandes de Xcas et les fonctions définies par l’utilisateur. Pour éviter le risque d’utiliser un nom de fonction de Xcas, il est conseillé de nommer les fonctions utilisateurs en utilisant une majuscule comme première lettre. Pour définir des fonctions (utilisateurs), on distinguera :
- les fonctions définies par une expression algébrique. Leur définition peut se faire simplement avec :=
- les fonctions qui nécessitent des calculs intermédiaires ou des structures de contrôle (test, boucle). Leur définition se fait au moyen d’un programme, en utilisant les instructions fonction...ffonction, local et retourne et les structures de contrôle qui seront détaillées au chapitre 32.
2.2.1 Quelques fonctions algébriques de Xcas
abs est la fonction valeur absolue.
ceil est le plus grand entier à l’argument.
cos est la fonction cosinus.
floor est la partie entière i.e.le plus grand entier à
l’argument.
frac est la partie fractionnaire d’un réel.
max est la fonction maximum pour une séquence de nombres réels.
min est la fonction minimum pour une séquence de nombres réels.
^
est la fonction puissance.
round est la fonction qui arrondit un réel en l’entier (resp le décimal) le plus proche.
sign est la fonction signe de l’argument et renvoie -1, 0 ou +1.
sin est la fonction sinus.
sqrt est la racine carrée.
tan est la fonction tangente.
2.2.2 Quelques fonctions aléatoires de Xcas
Nous donnons ci-après les fonctions aléatoires les plus fréquentes : pour des raisons de compatibilité avec le langage Python chacune des fonctions ci-dessous peuvent être préfixée par random. (par exemple on peut écrire random.randint() ou randint())
srand randseed RandSeed
srand ou randseed initialise la suite des nombres aléatoires :
srand ou randseedrenvoie un entier qui a servi à cette
initialisation.
On tape :
RandSeed
RandSeed(n) initialise les nombres aléatoires selon la valeur de
l’entier n
On tape :
random rand
random() ou rand() renvoie un nombre réel
(pseudo)-aléatoire de l’intervalle semi-ouvert .
On tape :
choice
choice(L) ou random(L) ou rand(L) renvoie un élément
tiré au hasard parmi les éléments de la liste L.
On tape :
randint
randint(a,b) renvoie un nombre entier (pseudo)-aléatoire
compris entre a et b (bornes incluses).
On tape :
shuffle
shuffle(L) applique une permutation aléatoire à la liste L.
On tape :
sample
sample(L,n) ou rand(n,L) renvoie si n<=size(L) n
éléments tirés au hasard et sans remise parmi les éléments de la
liste L et sinon renvoie une erreur.
On tape :
Remarque sample(L,len(L)) ou rand(dim(L),L) est identique
à shuffle(L).
On tape :
def testrand() : local L1,L2,L3,n1,n2,a1,a2; n1=random.random() n2=random() a1=random.randint(1,10) a2=randint(1,10) L1=sample([1,2,3,4,5,6],3) L2=random.sample([1,2,3,4,5,6],3) L3=shuffle([1,2,3,4,5,6]) return n1,n2,a1,a2,L1,L2,L3
onload
2.2.3 Définition d’une fonction algébrique d’une variable
Exemple :
On veut définir la fonction définie pour , par
.
est le nom de la fonction et est le nom de l’argument de (ici
est un réel), la valeur de la fonction est .
Remarque : En mathématique on dit que est une variable.
On tape simplement :
On pourrait aussi définir f1 par un programme
avec fonction...ffonction et retourne :
2.2.4 Définition d’une fonction algébrique de 2 variables
Exemple :
On veut définir la fonction définie pour , par
.
est le nom de la fonction et a 2 arguments réels : (en
mathématique on dit que sont les variables de ). La valeur
de la fonction est .
On tape simplement :
Ou on tape :
Valeur par défaut d’une variable
On peut définir la valeur par défaut de la variable y et ainsi
donner le même nom aux 2 fonctions et définies
précédement.
Ainsi la fonction sera définie par :
si elle a une variable alors et
si elle a 2 variables alors .
On tape alors simplement :
Ou on tape :
Autre exemple :
On veut définir la fonction définie pour par
où désigne le quotient et le reste de la division
euclidienne de par .
On tape simplement :
ou bien avec fonction...ffonction et retourne :
est le nom de la fonction, sont les noms des arguments de ( et
sont des entiers) et iquorem renvoie le quotient et le reste de la
division euclidienne de par sous la forme d’une liste.
On a aussi les instructions :
iquo(a,b) qui renvoie le quotient de la division euclidienne de par
.
irem(a,b) qui renvoie le reste de la division euclidienne de par .
et on a donc iquorem(a,b) est identique à [iquo(a,b),irem(a,b)].
2.2.5 Définition d’une fonction algébrique sans lui donner un nom
On peut définir une fonction sans la nommer, cela permet de la passer en argument d’une autre commande (par exemple une commande de recherche de racines par dichotomie).
-
syntaxe Xcas
Par exemple pour définir la fonction , on écrit simplement :
x->x^
2+1.
On tape :
(x->x^
2+1)(5)
Si on veut donner un nom à la fonction, on tape :
f(x):=x^
2+1
puis, on tape :
f(5)
On obtient :
26
- syntaxe Python
Par exemple pour définir la fonction , on écrit :
lambda x : x*x+1.
Le logiciel Xcas le traduit en On tape :
(lambda x : x*x+1)(5)
Si on veut donner un nom à la fonction, on tape :
def f(x):
return x*x+1
puis, on tape :
f(5)
On obtient :
26
Remarque : pas de risques de confusion !
Si vous avez une variable lambda qui est affectée, l’écriture :
(lambda x : x*x+1)(5), ne modifie pas cette variable.
On tape pour définir la fonction lambda :
On tape pour avoir la valeur de la fonction lambda en :
On tape pour définir une fonction sans nom :
On tape pour avoir sa valeur en :
2.2.6 Définition d’une fonction algébrique par morceaux avec quand
Exemple :
quand a 3 arguments : une condition et 2 expressions :
quand(Cond,Expr1,Expr2)
Si la condition Cond est vraie alors quand renvoie Expr1 et
si la condition Cond est fausse alors quand renvoie Expr2.
Soit la fonction Abs1 définie par Abs1 :
si on a Abs1 et
si on a Abs1.
On tape simplement :
ou bien avec fonction...ffonction et retourne :
- 1
- En toute rigueur ce sont des nombres écrits en base 2 et non en base 10 mais on peut l’ignorer au niveau du lycée
- 2
- On peut utiliser la syntaxe Python au sein de Xcas, mais il faut déclarer les variables locales avec local et utiliser ‘‘ comme délimiteur de chaînes de caractères.
Chapitre 3 Les instructions de programmation utilisées sur des exemples
On donne ici les instructions en syntaxe Xcas, on renvoie à la section A.1 pour la syntaxe Python.
3.1 Commentaires et documentation.
3.1.1 Les commentaires dans un programme.
Pour faire un commentaire, on écrit // (ou # si on a choisi la syntaxe Python), tout ce qui suit est le commentaire.
3.1.2 Documenter une fonction pour la commande help
help(nom_prog) renvoie la 1ère ligne d’un programme (hors commentaire). On peut donc indiquer à la personne utilisant un programme ce que fait ce programme en écrivant comme première ligne du programme une chaîne de caractères commentant la fonction.
3.1.3 Exemple
On tape en syntaxe Xcas :
fonction Delta(a,b,c) // discriminant "Delta(a,b,c) calcule b^2-4ac"; return b^2-4a*c; ffonction:;
3.2 Stocker une valeur dans une variable avec :=
L’opérateur infixé := stocke le deuxième argument dans la variable
donnée comme premier argument.
Exemple :
3.3 Enlever une valeur stockée dans une variable avec purge
L’instruction purge(a) permet d’enlever une valeur stockée dans la
variable a. La variable a redevient une variable libre i.e. une variable non affectée.
Exemple :
3.4 Suite d’instructions avec ; ou :;
Pour effectuer une suite d’instructions, il suffit de les écrire les unes
à la suite des autres, en terminant chaque instruction par ;
Exemple :
Remarque :
Lorsque la réponse est trop longue, on peut aussi utiliser :; et
on obtient Done comme réponse.
Notez qu’à l’intérieur d’un programme écrit en syntaxe Python
le ;
n’est pas nécessaire, il suffit de séparer les
instructions par un saut de ligne, en respectant la même indentation
que la première instruction.
3.5 L’instruction retourne ou return
L’instruction retourne arrête immédiatement
l’exécution du programme et renvoie la
valeur de l’instruction située après retourne.
Exemple 1:
3.6 L’instruction local
Exemple 2: notion de variables locales:
On veut, dans cet exemple, définir une fonction de deux variables
( et sont des entiers) qui renvoie le numérateur et le dénominateur
de la fraction simplifiée.
Pour cela il faut diviser et par leur pgcd qui est gcd(a,b).
si on tape :
cela nécessite de faire 2 fois le calcul de gcd(a,b).
Pour éviter cela, on va utiliser une variable locale qui servira à
stocker le calcul intermédiaire gcd(a,b)
avec l’instruction : c:=gcd(a,b) (:= est le
symbole de l’affectation et gcd(a,b) renvoie le pgcd de et ).
Cette variable n’est pas visible à l’extérieur du programme, les
modifications faites sur c dans le programme n’ont aucun effet
sur la variable c de la session.
On écrit alors local c; (ne pas oublier le ;)
On voit ainsi que les valeurs de a,b,c n’ont pas été changées par
l’exécution des fonctions h0 ou h.
3.7 L’instruction pour
Exemple 3 : notion de boucle pour
On veut, dans cet exemple, définir une fonction d’une variable (
est un entier) qui calcule la somme : .
Pour cela, on utilise une variable locale que l’on initialise à 0 :
S:=0;
puis on va faire étapes en utilisant cette variable locale .
va contenir successivement :
étape 1 donc contient 1 (0+1)
étape 2 donc contient 4 (1+3)
...
étape donc contient
...
étape donc contient
Pour décrire cela on utilise une boucle pour :
pour k de 1 jusque n faire S:=S+2*k-1; fpour;
Dans cette boucle k sera successivement égal à 1, 2, 3,...n.
On dit que l’instruction S:=S+2*k-1 figurant dans le corps la boucle
sera exécuté n fois.
Comment fonctionne cette boucle pour ?
- la variable k est initialisée à 1,
- les instructions du corps de la boucle sont effectuées (ici il y en a une seule S:=S+2*k-1),
- k est est incrémenté automatiquement de 1 (k:=k+1),
- le test k<=n est effectué :
si k<=n est vrai, les instructions du corps de la boucle sont à
nouveau effectuées etc ...
sinon on effectue les instructions qui suivent fpour.
On tape :
fonction Simpair(n) local S,k; S:=0; pour k de 1 jusque n faire S:=S+2*k-1; fpour; retourne S; ffonction:;
Intermède mathématique
Au vue des résultats obtenus pouvez-vous deviner la valeur de
s(100) ?
Pouvez-vous deviner et montrer la formule qui donne s(n) ?
On devine :
On sait que pour tout on a :
et
Donc :
(),
(),
(),
...
...
Donc :
En classe de terminales, on peut montrer cette formule par récurrence :
si alors on a :
La formule est donc montrée par récurrence.
3.8 L’instruction pour avec un pas
Exemple 4 : notion de liste et boucle pour avec un pas
On va tout d’abord faire le programme du ticket de caisse lors d’achats dans
un magasin qui ne pratique pas de réduction pour les achats en gros.
Le programme du ticketcaisse a comme paramètre une liste L donnant
le nombre d’un même article suivi du prix de cet article, par exemple :
si L:=[2,24,1,15,5,10] cela signifie qu’il y 2 articles à 24 euros, 1
article à 15 euros et 5 articles à 10 euros.
Soit n:=dim(L), dans cet esxemple n:=6.
On va parcourir la liste avec une variable k : L[k] sera le nombre
d’articles ayant comme prix L[ k+1] : il faut donc, dans cet exemple,
que k prenne successivement pour valeur 0, 2, 4=n-2.
Pour cela on initialise la somme à payer avec 0 : S:=0 puis
on utilise une boucle pour avec un pas de 2 :
pour k de 0 jusque n-2 pas 2 faire S:=S+L[k]*L[k+1]; fpour;
Dans cette boucle pour, la variable k est initialisée à
0, puis les instructions du corps de la boucle sont effectuées,
puis k est incrémenté automatiquement de 2 (k:=k+2),
puis on fait le test k<=n-2 si oui les instructions du corps de la boucle
sont à nouveau effectuées etc ... sinon on effectue les instructions qui
suivent fpour.
fonction Ticketcaisse(L) local S,n,k; n:=dim(L); S:=0; pour k de 0 jusque n-2 pas 2 faire S:=S+L[k]*L[k+1]; fpour; retourne S; ffonction:;
3.9 L’instruction si
Exemple 5 : notion de test
Dans un magasin on favorise les achats en gros :
si un article a a comme prix affiché P euros, pour l’achat d’au
moins 3 articles a, vous avez une réduction de 10%.
On veut, dans cet exemple, définir une fonction Prix de 2 variables
n (n est un entier) et P un réel qui calcule le prix de
n article(s).
Pour cela, on utilise :
une variable locale S qui sera la somme à débourser et
le test :
si <condition> alors <instruction1;> sinon <instruction2;> fsi;
Comment fonctionne le test si ?
- On évalue la condition : une condition a 2 valeurs possibles vrai ou faux c’est ce que l’on nomme un booléen,
- Si la condition est vraie : on effectue les <instruction1;>, et si la condition est fausse : on effectue les <instruction2;>,
- On effectue ensuite les instructions qui suivent fsi;.
On tape pour avoir le prix de n fois le même article de prix P :
fonction Prix(n,P) local S; si n>=3 alors S:=n*P*0.9; sinon S:=n*P; fsi; retourne S ffonction:;
onload
3.10 Utiliser une fonction utilisateur dans un programme
Exemple 6 :
On veut faire le programme du ticket de caisse lorsque le magasin pratique
l’achat en gros (la liste L doit spécifier le nombre d’un même
article de prix ).
En utilisant la fonction Prix(n,P) écrite précédemment (cf
3.9), modifier le programme précédent lorsque le magasin
pratique l’achat en gros.
Solution :
fonction Ticketengros(L) local S,n,k; n:=dim(L); S:=0; pour k de 0 jusque n-2 pas 2 faire S:=S+Prix(L[k],L[k+1]); fpour; retourne S; ffonction:;
3.11 L’instruction tantque
Exemple 7 : Notion de boucle tantque
On utilise une boucle tantque lorsque l’on ne connaît pas à
l’avance le
nombre d’itérations à effectuer et que l’on arrête les itérations quand
une condition devient fausse :
tantque <condition> faire <instructions> ftantque;
Comment fonctionne une boucle tantque ?
- On évalue la condition : une condition a 2 valeurs possibles vrai ou faux c’est ce que l’on nomme un booléen,
- Si la condition est vraie : on effectue <instructions;>, et si la condition est fausse : on effectue les instructions qui suivent ftantque;.
ou bien on peut dire en langage courant que :
<condition> est une condition de continuation de la boucle.
tant que la condition est vérifiée, on fait les instructions de la boucle.
Traduction d’une boucle pour en une boucle tantque
Soit une liste L de nombres réels.
On veut faire la somme des réels de L.
On tape en utilisant une boucle pour :
fonction Somme(L) local n,j,S; n:=dim(L); S:=0; pour j de 0 jusque n-1 faire S:=S+L[j]; fpour; retourne S; ffonction:;
onload
On tape en utilisant une boucle tantque :
fonction Somme1(L) local n,j,S; n:=dim(L); S:=0; j:=0; tantque j <= n-1 faire S:=S+L[j]; j:=j+1; ftantque; retourne S; ffonction:;
On peut aussi écrire Somme2, mais Attention
à l’ordre des instructions de la boucle tantque et au test d’arrêt :
fonction Somme2(L) local n,j,S; n:=dim(L); j:=0; S:=L[0]; tantque j < n-1 faire j:=j+1; S:=S+L[j]; ftantque; retourne S; ffonction:;
Exemple 8 : autre exemple de boucle tantque
En fin de mois, Paul n’a plus qu’une somme a dans son porte-monnaie.
Paul fait sa liste de courses Lc en mettant au début ce qu’il
veut vraiement acheter et à la fin de sa liste, il met les achats qu’il doit
faire à plus long terme. Dans le magasin (qui ne fait pas de réduction), sa
liste de courses Lc devient une liste de prix L.
Dans ce cas, on ne peut pas utiliser une boucle avec pour car on ne
sait pas au départ combien de fois on doit effectuer la boucle.
On note S la variable qui stockera successivement la somme des prix des
premiers éléments de L : pour cela on utilise la fonction
Somme(L) écrite précedemment (cf 3.11).
Il veut faire un programme qui arrête sa liste dès que S>a en coupant
L en 2 listes :
La liste des objets de ce qu’il achète réellement pour un montant
S<=a et Lfin liste des objets qu’il n’achète pas (Lfin est
une liste vide lorsque Somme(L)<=a).
Ticketfindemois(L,a) doit renvoyer La,Lfin,P où P est la
somme à payer.
"arrêt" se traduit ici par Lfin ==[] ou S>a donc
"continuation" se traduit ici par Lfin!=[] et S<=a.
On teste tout d’abord si Paul a assez d’argent pour payer toute sa liste :
pour cela, on utilise le programme Somme précédent
(cf 3.11).
Paul a assez d’argent pour payer toute sa liste lorsque Somme(L)<=a
et alors on a La :=L, Lfin :=[] et P:=Somme(L).
Si Somme(L)>a, Paul n’a pas assez d’argent donc Lfin!=[] est
vrai et la condition d’arrêt est : S<=a.
On écrit Ticketfindemois(L,a) pour que k soit le
nombre d’articles achetés lorsqu’ on sort du tantque :
fonction Ticketfindemois(L,a) local S,n,k,Lfin,La; S:=Somme(L); si S <=a alors retourne L,[],S fsi; n:=dim(L); k:=0; S:=L[0]; tantque S <=a faire k:=k+1; S:=S+L[k]; ftantque; La:=gauche(L,k); Lfin:=droit(L,n-k); retourne La,Lfin,S-L[k]; ffonction:;
On peut aussi écrire mais Attention à
l’ordre des instructions dans le tantque.
fonction Ticketfindemois1(L,a) local S,n,k,Lfin,La; S:=Somme(L); si S <=a alors retourne L,[],S fsi; n:=dim(L); S:=0; k:=0; tantque S <=a faire S:=S+L[k]; k:=k+1; ftantque; La:=gauche(L,k-1); Lfin:=droit(L,n-k+1); retourne La,Lfin,S-L[k-1]; ffonction:;
Dans Ticketfindemois1, c’est k-1 et non k, qui est pas la
valeur du nombre d’articles achetés lorsqu’on sort du tantque.
En effet, lorsqu’on s’arrête devient
supérieur à : il ne faut donc pas acheter l’article L[k].
Donc La:=gauche(L,k-1); et Lfin:=droit(L,n-k+1).
Remarque
On aurait pu aussi écrire sans utiliser Somme
mais c’est plus compliqué car la condition du tantque porte sur k
et sur S !!!
fonction Ticketfindemois2(L,a) local S,n,k,La,Lfin,P; n:=dim(L); k:=0; S:=L[0]; tantque S<=a et k<n-1 faire k:=k+1; S:=S+L[k]; ftantque; si S<=a alors retourne L,[],S fsi; La:=gauche(L,k); Lfin:=droit(L,n-k); P:=S-L[k]; retourne La,Lfin,P; ffonction:;
À chaque étape on a :
Au début, on a :
k:=0;S:=L[0]; donc S est le prix de 1 article.
lorsqu’on fait fois la boucle on a :
S:=L[0]+..L[k]; donc S est la somme de k+1 articles.
Quand on sort du tantque on a :
soit S<=a est vrai, donc k==n-1 est vrai (puisque
(S<=a et k<n-1)==faux).
S est donc la somme de toute la liste L i.e. S est la somme
à payer pour l’achat de n articles i.e. Paul peut acheter toute
sa liste de courses.
soit S>a et k<=n-1 alors S représente la somme des prix des
k+1 premiers articles (k+1 car on a commencé à l’article
k=0. Mais Paul ne peut pas acheter le dernier
article puisque S>a. Le prix P représente la somme des prix des
k premiers articles i.e.des articles L[0],..L[k-1] ou encore des
artices de la liste gauche(L,k) donc P:=S-L[k].
3.12 Interruption d’une boucle
Si on utilise retourne à l’intérieur d’une boucle dans une fonction, celle-ci est interrompue. Ceci permet de transformer des boucles "tantque" en boucle "pour" souvent plus lisibles.
Reprenons l’exemple ci-dessus,
on remarque que la boucle tantque utilise un compteur k
qu’on incrémente à chaque itération comme dans une boucle pour.
Il est donc naturel d’essayer de réécrire cette fonction avec
une boucle pour. Il suffira de tester dans le corps de
la boucle si la somme (avec le nouvel article)
dépasse le contenu du porte-monnaie, dans ce cas il faut s’arrêter
sans acheter le nouvel article, on interrompt la boucle et on renvoie les
résultats (on calcule la somme au fur et à mesure et donc on n’utilise pas
la fonction Somme).
Dans Ticketfindemois3, c’est k, qui est pas la
valeur du nombre d’articles achetés lorsqu’on sort du pour.
En effet, lorsqu’on s’arrête devient
supérieur à : il ne faut donc pas acheter l’article L[k].
Donc La:=gauche(L,k); et Lfin:=droit(L,n-k).
fonction Ticketfindemois3(L,a) local k,n,S,La,Lfin; n:=dim(L); S:=0; pour k de 0 jusque n-1 faire si S+L[k]>a alors La:=gauche(L,k); Lfin:=droit(L,n-k); retourne La,Lfin,S; fsi; S:=S+L[k]; fpour; retourne L,[],S; ffonction:;
Cette méthode s’applique pour toute boucle “tantque” dont on peut prévoir à priori un majorant du nombre d’itérations. On peut d’ailleurs aussi l’utiliser si on se fixe un nombre maximal d’itérations qui tient compte du temps d’exécutions, typiquement en Xcas de l’ordre du million d’itérations si on veut un résultat en moins de quelques secondes.
Remarque : si on ne veut pas quitter la fonction, il est quand même possible d’interrompre la boucle prématurément en utilisant l’instruction break.
3.13 Exemple 9 : autre exemple de boucle tantque
Pour avoir des clients le dimanche matin, le magasin de Paul offre selon
les dimanches une réduction immédiate qui varie selon le montant
des achats par exemple une réduction de 10 euros dès 60 euros
d’achats, ou une réduction de 5 euros dès 50 euros d’achats etc...
Ce magasin ne pratique pas de réduction pour des achats en gros.
Pour être sûr de bénéficier de la réduction, Paul fait sa liste
de courses en mettant au début ce qu’il veut vraiement acheter et à la
fin de sa liste, il met les achats qu’il doit faire à plus long terme
(contrairement au programme précédent, on suppose ici
que Paul a suffisamment d’argent).
Attention
On suppose ici que la liste de courses est constituée par une suite des
nombres , où est le nombre d’achats du même article de prix .
Il veut faire un programme qui arrête sa liste dès que en coupant
en 2 listes liste des objets de ce qu’il achète réellement pour
un montant avant réduction et liste des objets qu’il
n’achète pas ( est éventuellement une liste vide).
Paul veut que son programme ait paramètres et qu’il renvoie :
, , , .
Dans ce cas, on ne sait pas au départ combien de fois on doit effectuer la
boucle.
Mais on sait quand on doit s’arrêter :
on arrête la boucle lorsque le prix de la liste complète
n’atteint pas le montant ou dés que le prix du début de
vérifie S>=a.
On utilise pour cela une boucle tantque :
tantque <condition> faire <instructions> ftantque;
Cela veut dire :
tant que "non arrêt", on fait les instructions de la boucle.
"arrêt" se traduit ici par Lfin ==[] ou S>=a donc,
"non arrêt" se traduit ici par Lfin!=[] et S<a.
Attention la variable qui va parcourir la liste L devra être
initialisée (ici k:=0;) et modifiée dans le corps de la boucle (ici
k:=k+2;).
La fonction Ticketdimanche a 3 paramètres L,a,r et renvoie
la liste des courses qui ont été prises en compte,
la liste des courses qui n’ont pas été prises en compte (cette liste peut être vide si S<=a)
la somme S des achats sans la remise et
la somme S-r à payer.
On tape :
fonction Ticketdimanche(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; k:=0; tantque k<n et S<a faire S:=S+L[k]*L[k+1]; k:=k+2; ftantque; La:=gauche(L,k); Lfin:=droit(L,n-k); si S<a alors r:=0; fsi; retourne La,Lfin,S,S-r; ffonction:;
Traduction du “tantque” en “pour”
On remarque que la boucle “tantque” a un compteur k, on peut donc
la transformer en boucle “pour” avec sortie prématurée de la boucle
lorsque .
Attention gauche(L,k) représente les k premiers
éléments de L i.e c’est L[0]..L[k-1].
Ici, on sort de la boucle pour lorsque S>=a donc pour avoir la
réduction il faut acheter L[k] fois l’article L[k+1], c’est à
dire prendre en compte la liste L[0].. L[k+1] qui est une liste de
k+2 éléments c’est donc gauche(L,k+2).
fonction Ticketdimanche1(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; pour k de 0 jusque n-1 pas 2 faire S:=S+L[k]*L[k+1]; si S>=a alors La:=gauche(L,k+2); Lfin:=droit(L,n-k-2); retourne La,Lfin,S,S-r; fsi; fpour; retourne L,[],S,S; ffonction:;
3.14 Exemple 10 : encore un autre exemple de boucle tantque
Maintenant le magasin de Paul favorise aussi les achats en gros :
10% de réduction lorsque on achète 3 fois le même produit.
En plus il offre selon les dimanches une réduction immédiate qui varie
selon le montant des achats, par exemple une réduction de 10 euros dès
60 euros d’achats.
Modifier les programmes précédents pour tenir compte des achats en
gros. On utilise ici la fonction Prix écrite précédemment
(cf 3.9).
Attention comme précédemment on suppose ici que la liste de courses
est constituée par une suite des nombres , où est le nombre
d’achats du même article de prix .
On tape :
fonction Ticketdimgros(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; k:=0; tantque k < n et S<a faire S:=S+Prix(L[k],L[k+1]); k:=k+2; ftantque; La:=gauche(L,k); Lfin:=droit(L,n-k); si S < a alors r:=0 ;fsi; retourne La,Lfin,S,S-r; ffonction:;
Transformation en boucle “pour”
fonction Ticketdimgros1(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; pour k de 0 jusque n-1 pas 2 faire S:=S+Prix(L[k],L[k+1]); si S>=a alors La:=gauche(L,k+2); Lfin:=droit(L,n-k-2); retourne La,Lfin,S,S-r; fsi; fpour; retourne La,[],S,S; ffonction:;
3.15 Exercice : Algorithme de tracé de courbe
Soit la fonction f définie sur [a,b].
On veut tracer le graphe de cette fonction sur l’intervalle [a,b].
En partageant [a,b] en n parties égales on obtient :
a=a0,a1=a+h,a2=a+2h, ...b=a+n*h avec h=(b-a)/n.
Le graphe sera obtenu en reliant les points de coordonnées [a f(a)]
[a1 f(a1)] etc ... par des segments.
On tape :
fonction Graphe(f,a,b,n) local L,h,k; L:=NULL; h:=(b-a)/n; pour k de 0 jusque n-1 faire L:=L,segment(point(a,f(a)),point(a+h,f(a+h))); a:=a+h; fpour; retourne L; ffonction:;
3.16 Mettre au point un programme
Lorsqu’on écrit un programme, il y a deux étapes à franchir :
- Écrire un programme syntaxiquement correct. Quand on tape sur le bouton ok, il “compile” (plus préciśement il est interprété). Les erreurs à ce stade sont appelées erreurs de syntaxe. Certaines interfaces de Xcas essaient de placer le curseur à l’endroit de l’erreur, d’autres affichent le numéro de ligne et colonne de l’erreur. Il faut garder en tête que l’erreur peut être située avant l’endroit indiqué par le curseur ou dans le message d’erreur.
- S’assurer que le programme fait bien ce qu’on attend de lui.
Si ce n’est pas le cas on parle d’erreur
d’exécution
(en anglais runtime error). Pour corriger ce type d’erreur, Xcas
propose une commande appelée
debug
. Au lieu d’appeler directement le programme (par exemple au lieu deGraphe(f,-2,2,100)
on tapedebug(Graphe(f,-2,2,100))
. Le debogueur affiche alors la liste des variables locales et leur valeur ainsi que la prochaine ligne du programme qui sera exécutée. On peut alors exécuter ligne par ligne le programme avec des arguments pour lequel on connait la réponse, regarder l’évolution des valeurs des variables et comprendre plus facilement pourquoi le programme ne fait pas ce qui est attendu.
Chapitre 4 Résolution d’équations
4.1 Encadrer une racine d’une équation par dichotomie
Algorithme de dichotomie
On suppose que la fonction est continue sur
l’intervalle et que et
(si et on peut se ramener au cas précédent
en échangeant et ).
On en déduit que s’annule pour avec ,
on cherche une valeur approchée de .
Pour avoir une meilleur approximation de , on cherche le signe de où est le milieu de . Si alors et on est content ! Si , il est de même signe que , on peut donc remplacer par , sinon est de même signe que , on peut remplacer par . On recommence le processus jusquà ce que ou (avec par exemple ).
fonction Dichotomie0(f,a,b,n) local c; si f(a)*f(b)>0 alors retourne [] fsi; si f(a)==0 alors retourne [a] fsi; si f(b)==0 alors retourne [b] fsi; si f(a)>0 alors a,b:=b,a; fsi; // echange a et b pour avoir f(a)<=0 tantque abs(b-a)>10^(-n) faire c:=evalf(a+b)/2; si f(c)=0 alors retourne [c] fsi; si f(c)<0 alors a:=c; sinon b:=c; fsi; // on a f(a)<=0 ftantque retourne [c]; ffonction:;
Notez le test d’arrêt qui utilise car et ont peut-être
été échangés.
Exemple :
On peut rajouter en début de programme
un test sur n pour que le nombre d’itérations
ne soit pas trop grand, par exemple
si n>12 alors n:=12; fsi;
On peut aussi utiliser une variable locale pour ne faire qu’une seule fois
le calcul de et de . Ce qui donne le programme suivant :
fonction Dichotomie1(f,a,b,n) local c,fc,eps; si f(a)*f(b)>0 alors retourne [] fsi; si f(a)==0 alors retourne [a] fsi; si f(b)==0 alors retourne [b] fsi; si f(a)>0 alors a,b:=b,a; fsi; // echange a et b pour avoir f(a)<=0 si n>12 alors n:=12; fsi; eps:=10^(-n); tantque abs(b-a)>eps faire c:=evalf(a+b)/2; fc:=f(c); si fc=0 alors retourne [c] fsi; si fc<0 alors a:=c; sinon b:=c; fsi; // on a f(a)<0 ftantque retourne [c]; ffonction:;
Il n’est pas indispensable de tester que , et (comme cela n’arrive pratiquement jamais, c’est donc inefficace de le faire), on peut supprimer ces lignes. Il faut alors utiliser comme test (au lieu de ) pour conserver comme invariant de boucle et .
fonction Dichotomie(f,a,b,n) local c,eps; si f(a)*f(b)>0 alors retourne [] fsi; si f(a)>0 alors a,b:=b,a; fsi; // echange a et b pour avoir f(a)<=0 si n>12 alors n:=12; fsi; eps:=10^(-n); tantque abs(b-a)>eps faire c:=evalf(a+b)/2; // invariant f(a)<=0 et f(b)>0 si f(c)<=0 alors a:=c; sinon b:=c; fsi; ftantque retourne [c]; ffonction:;
Traduction du “tantque” en “pour”
On observe qu’à chaque itération de la boucle
on divise la longueur de l’intervalle par 2, le nombre
d’itérations ne peut pas être très grand.
On peut donc transformer la boucle “tantque” en boucle “pour”
en se fixant à priori un nombre maximal d’itérations ce qui
évitera d’ailleurs d’avoir une boucle qui ne se termine jamais. On
montrera plus bas que 2100 itérations
suffisent en calcul approché.
fonction Dichotomie2(f,a,b,n) local c,k,eps; eps:=10^-n; si f(a)*f(b)>0 alors retourne []; fsi; pour k de 1 jusque 2100 faire c:=(a+b)/2.0; // invariant f(a)*f(b)<=0 si b-a<eps alors retourne [c]; fsi; si f(a)*f(c)<=0 alors b:=c; sinon a:=c; fsi; fpour; retourne [c]; ffonction:;
Exercice : modifier le programme pour ne pas faire le calcul de
dans la boucle.
Remarque avancée :
si on connaît les logarithmes, on peut calculer
le nombre d’itérations
pour que en résolvant cette équation.
On peut aussi observer que les calculs se font en approché, dans Xcas
le plus grand nombre représentable par défaut
est evalf(2^(1024-1)
, donc la taille
du plus grand intervalle est (légèrement inférieure à)
. Le plus petit réel strictement positif représentable
est evalf(2^(-1069))
. Comme on divise par 2 la taille
de l’intervalle à chaque itération, le nombre maximal d’itérations
est au plus 1025+1069=20941.
Au-delà, soit et sont représentés
par le même nombre flottant (et le test sera
donc vrai) soit ils ne différeront que par leur dernier bit de mantisse,
et dans ce cas sera arrondi vers ou vers et la boucle
tanque continuera indéfiniment.
La majoration est le plus souvent très pessimiste, par exemple
si et ils sont déjà représentés avec le même
exposant et le nombre d’itérations sera limité par 48.
fonction Dichotomie3(f,a,b,n) local c,k,N; si f(a)*f(b)>0 alors retourne []; fsi; N:=ceil(log((b-a)/10^(-n))/log(2)); si N>2100 alors N:=2100 fsi; pour k de 1 jusque N faire c:=(a+b)/2.0; // f(a)*f(b)<=0 si f(a)*f(c)<=0 alors b:=c; sinon a:=c; fsi; fpour; retourne [c]; ffonction:;
Exercice : Modifier la fonction ci-dessus pour calculer une
seule fois par itération, c’est-à-dire qu’on calcule et
mais qu’on ne recalcule pas .
Première méthode :
On introduit 3 variables locales fa, fb, fc contenant les valeurs
de , , . On en profite pour tester au passage si .
fonction Dichotomie4(f,a,b,n) local c,k,N,fa,fb,fc; fa:=f(a); fb:=f(b); si fa*fb>0 alors retourne []; fsi; si fa==0 alors retourne [a] fsi; si fb==0 alors retourne [b] fsi; N:=ceil(log((b-a)/10^(-n))/log(2)); si N>2100 alors N:=2100 fsi; pour k de 1 jusque N faire c:=(a+b)/2.0; fc:=f(c); si fc==0 alors retourne [c] fsi; si fa*fc<0 alors b:=c; sinon a:=c; fa:=fc; fsi; fpour; retourne [c]; ffonction:;
Deuxième méthode :
On échange le rôle de et si .
fonction Dichotomie5(f,a,b,n) local c,k,N; si f(a)*f(b)>0 alors retourne []; fsi; N:=ceil(log(abs(b-a)/10^(-n))/log(2)); si N>2100 alors N:=2100 fsi; si f(a)>0 alors a,b:=b,a; fsi; pour k de 1 jusque N faire c:=(a+b)/2.0; // invariant f(a)<=0 et f(b)>0 si f(c)<=0 alors a:=c; sinon b:=c; fsi; fpour; retourne [c]; ffonction:;
Ce programme est optimal.
On peut vérifier ces résultats en utilisant la commande fsolve de
Xcas qui effectue la résolution numérique d’une équation :
4.2 Résoudre dans une équation se ramenant au premier degré ou au degré 2
On considère une équation qui se ramène au premier ou au deuxième
degré.
Si cette équation se ramène au premier degré, elle est de la forme :
donc cette équation a une solution qui est:
x0:=-b/a.
Si cette équation se ramène au deuxième degré, elle est de la forme :
^
2+b*x+c=0 avec a!=0donc :
-
si =b
^
2-4*a*c>0 il y a 2 solutions qui sont :
x1:=(-b+)/(2*a) et x2:=(-b-)/(2*a). - si =b
^
2-4*a*c=0 il y a 1 solution qui est :
x1 et x1:=-b/(2*a) - si =b
^
2-4*a*c<0 il n’y a pas de solution réelle.
On tape :
fonction Solution12(Eq,Var) local a,b,c,d; Eq:=normal(gauche(Eq)-droit(Eq)); si degree(Eq,Var)==0 alors si (Eq==0) alors retourne "infinite de solution" ; sinon retourne "pas de solution" ; fsi; fsi; si degree(Eq,Var)==1 alors //a:=coeff(Eq,Var,1);b:=coeff(Eq,Var,0); b:=subst(Eq,Var=0); a:=subst(Eq,Var=1)-b; retourne normal([-b/a]); fsi; si degree(Eq,Var)==2 alors //a:=coeff(Eq,Var,2);b:=coeff(Eq,Var,1);c:=coeff(Eq,Var,0); c:=subst(Eq,Var=0); d:=subst(Eq,Var=1); b:=(d-subst(Eq,Var=-1))/2; a:=d-b-c; d:=b^2-4*a*c; si d>0 alors retourne simplify([(-b+sqrt(d))/(2*a),(-b-sqrt(d))/(2*a)]);fsi; si d==0 alors retourne simplify([-b/(2*a)]); fsi; retourne []; fsi; retourne "degree >2"; ffonction:;
onload
4.3 Résoudre un système de deux équations du premier degré à deux inconnues.
On veut résoudre le système de deux équations du premier degré
à deux inconnues . On notera a1, b1, c1, a2, b2, c2 les
coefficients des équations dans les programmes.
Pour éviter d’étudier des cas particuliers inintéressants, on va
supposer que et .
Dans ce cas et
sont les équations de 2 droites et .
Solution géométrique
-
Si et sont concourantes, il y a une seule solution.
Pour la déterminer, on peut utiliser la commande
solve de Xcas
On justifiera ce résultat plus bas. - Si et sont parallèles il n’y a pas de solution, sauf si
et sont confondues, il y a une infinité de solutions. On
va montrer que cela se produit si et seulement si .
En effet, et sont parallèles lorsque les coefficients et sont proportionnels i.e si il existe tel que :
ce qui entraine : Réciproquement, si alors et sont parallèles ou confondues.
En effet :- Si (resp ) alors (resp ) puisque et , donc et sont parallèles à l’axe des (resp ).
- Si et alors ce qui signifie que
et sont parallèles ou confondues
( et sont confondues lorsque les coefficients
et sont proportionnels i.e si il existe tel que :
).
On commence par écrire un programme dans le cas où les droites et sont concourrantes.
fonction Intersection1(Eq1,Eq2,Var1,Var2) local a1,b1,c1,a2,b2,c2; Eq1:=normal(gauche(Eq1)-droit(Eq1)); Eq2:=normal(gauche(Eq2)-droit(Eq2)); a1:=coeff(Eq1,Var1,1); a2:=coeff(Eq2,Var1,1); b1:=coeff(Eq1,Var2,1); b2:=coeff(Eq2,Var2,1); si normal(a1*b2-a2*b1)==0 alors retourne "Cas non traite : Eq1 ou Eq2 n'est pas une "+ "equation de droite ou droites paralleles"; fsi; c1:=subst(Eq1,[Var1,Var2],[0,0]); c2:=subst(Eq2,[Var1,Var2],[0,0]); print("droites concourantes"); retourne [normal((-b2*c1+b1*c2)/(a1*b2-a2*b1)), normal((a2*c1-a1*c2)/(a1*b2-a2*b1))]; ffonction:;
Exercice : modifiez le programme ci-dessus pour éviter de calculer
plusieurs fois , en stockant sa valeur dans une variable
locale.
Correction de l’exercice :
fonction Intersection2(Eq1,Eq2,Var1,Var2) local a1,b1,c1,a2,b2,c2,d; Eq1:=normal(gauche(Eq1)-droit(Eq1)); Eq2:=normal(gauche(Eq2)-droit(Eq2)); a1:=coeff(Eq1,Var1,1); a2:=coeff(Eq2,Var1,1); b1:=coeff(Eq1,Var2,1); b2:=coeff(Eq2,Var2,1); d:=normal(a1*b2-a2*b1); si d==0 alors retourne "Cas non traite : Eq1 ou Eq2 n'est pas une"+ " equation de droite ou droites paralleles ou confondues"; fsi; c1:=subst(Eq1,[Var1,Var2],[0,0]); c2:=subst(Eq2,[Var1,Var2],[0,0]); print("droites concourantes"); retourne [normal((-b2*c1+b1*c2)/d), normal((a2*c1-a1*c2)/d)]; ffonction:;
Voici maintenant un programme qui teste que les équations
entrées sont bien des équations de droite et traite aussi
le cas des droites parallèles ou confondues :
fonction Intersection(Eq1,Eq2,Var1,Var2) local a1,b1,c1,a2,b2,c2,d; Eq1:=normal(gauche(Eq1)-droit(Eq1)); si degree(Eq1,Var1)>1 et degree(Eq1,Var2)>1 alors retourne "pas de degre 1"; fsi; Eq2:=normal(gauche(Eq2)-droit(Eq2)); si degree(Eq2,Var1)>1 et degree(Eq2,Var2)>1 alors retourne "pas de degre 1"; fsi; a1:=coeff(Eq1,Var1,1); a2:=coeff(Eq2,Var1,1); b1:=coeff(Eq1,Var2,1); b2:=coeff(Eq2,Var2,1); si [a1,b1]==[0,0] ou [a2,b2]==[0,0] alors retourne "Eq1 ou Eq2 est nulle"; fsi; c1:=subst(Eq1,[Var1,Var2],[0,0]); c2:=subst(Eq2,[Var1,Var2],[0,0]); d:=normal(a1*b2-a2*b1); si d!=0 alors print("droites concourantes"); retourne [normal((-b2*c1+b1*c2)/d), normal((a2*c1-a1*c2)/d)]; fsi; si a1!=0 et a2!=0 alors si c1*a2-c2*a1==0 alors print("droites confondues"); retourne [normal(-c1/a1),Var2]; sinon print("droites paralleles"); retourne [] ; fsi; fsi; si b1!=0 et b2!=0 alors si c1*b2==c2*b1 alors print("droites confondues"); retourne [Var1,normal(-c1/b1)]; sinon print("droites paralleles"); retourne []; fsi; fsi; ffonction:;
On vérifie avec Xcas :
Justification de la solution lorsque et sont concourantes
On a vu que c’était le cas si et seulement si
.
-
Si on a donc l’abscisse du point
d’intersection de et vérifie :
donc et finalement : On remplace dans l’expression de en fonction de : Donc - Si , on va voir que les mêmes formules s’appliquent. En effet l’abscisse du point d’intersection de et vérifie donc Comme , on a
- 1
- Il faut ajouter 5 pour un langage traditionnel où la mantisse a 53 chiffres significatifs. Attention, ceci n’est plus valable dans Xcas si on modifie la valeur de Digits
Chapitre 5 Les figures en géométrie plane avec Xcas
5.1 Le point : point et le segment : segment
point a comme arguments l’abscisse et l’ordonnée du point.
point trace le point à l’aide d’une croix sur l’écran de
géométrie .
Si on a donné un nom au point (par ex A:=point(1,1); ou
A:=point([1,1]);) ce nom sera affiché à côté de la croix.
segment a comme argument 2 points.
segment trace le segment reliant ces 2 points sur l’écran de
géométrie .
5.2 Les coordonnées d’un point : coordonnees
coordonnees a comme argument 1 point.
coordonnees renvoie la liste constitué de l’abscisse et de l’ordonnée
du point.
5.3 La droite et son équation : droite et equation
droite a comme argument 2 points.
droite trace la droite passant par ces 2 points sur l’écran de
géométrie .
equation a comme argument une droite.
equation renvoie l’équation de cette droite
5.4 Ligne brisée : polygone_ouvert
polygone_ouvert a comme argument une liste L de points.
polygone_ouvert trace la ligne brisée joignant les points
L[k] et L[k+1] pour k=0..dim(L)-2.
5.5 Les polygones : triangle, carre, polygone
triangle a comme argument 3 points.
triangle trace le triangle défini par ces 3 points sur l’écran de
géométrie .
carre a comme argument 2 points.
carre trace le carré direct défini par ces 2 points sur l’écran de
géométrie .
polygone a comme argument une liste de points.
polygone trace le polygone fermé défini par cette liste de points sur
l’écran de géométrie .
Exercice
Faire un programme qui trace les polygones réguliers connaissant son centre
, un de ses sommets et le nombre de côtés.
fonction Isopolygonec(C,A,n) local L,k; L:=NULL; L:=A; pour k de 0 jusque n-1 faire L:=L,C+(A-C)*exp(2*i*k*pi/n); fpour; retourne polygone(L); ffonction:;
onload
Xcas, la fonction isopolygone a 3 arguments : 2 sommets
et le nombre de côtés ou bien le centre du polygone, un sommet
et un nombre négatif qui a comme valeur absolue le nombre de
côtés.
Avec Xcas, on tape :
qui est identique à :
5.6 Le cercle et son équation : cercle et equation
Si le cercle est défini par son centre et son rayon alors
cercle a pour argument un point et un réel r.
cercle trace le cercle de centre ce point et de rayon abs(r)
sur l’écran de géométrie .
Si le cercle est défini par son diamètre alors
cercle a pour argument 2 points.
cercle trace le cercle de diamètre ces 2 points.
5.7 Les tangentes à un cercle passant par un point et leurs équations
Si C est un cercle et B un point situé à l’extérieur de
(resp sur) C alors tangent(C,B) trace les (resp la) tangente(s) à
C passant par B.
5.8 Exercice : les lunules d’Hippocrate
Ce théorème, très ancien, a été démontré par Hippocrate de Chios
(-500) (Ne pas le confondre avec Hippocrate de Cos, le médecin), qui étudia
aussi la duplication du cube, c’est-à-dire le calcul de la racine cubique de 2.
Hippocrate recherchait alors la quadrature du cercle et pensait que la
quadrature de ses lunules allait le rapprocher du but.
Une lunule est une portion de surface délimitée par deux arcs de cercles
non concentriques de rayons différents. Ces arcs ont mêmes extrémités et
forment un croissant de lune en forme de ménisque : convexe d’un côté et
concave de l’autre.
5.8.1 Exercice 1
Soit le triangle rectangle en et le cercle circonscrit
à (de diamètre ).
La lunule est la figure formée par le demi-disque de diamètre
extérieur au triangle , auquel on enlève son intersection avec le disque
délimité par .
La lunule est la figure formée par le demi-disque de diamètre
extérieur au triangle , auquel on enlève son intersection avec le
disque délimité par .
Ces deux lunules (en jaune sur la figure ci-dessous) sont appelées
lunules d’Hippocrate.
Montrer que la somme des aires de ces deux lunules et de (en
jaune) est égale à l’aire du triangle (en rouge).
On tape pour faire la figure :
fonction Lunule1() local A,B,C,L; L:=NULL; A:=point(0); B:=point(2,affichage=quadrant1); C:=point(3*i,affichage=quadrant1); L:=L,cercle(A,C,-pi/2,pi/2,affichage=3+rempli); L:=L,cercle(A,B,-pi,0,affichage=3+rempli); L:=L,cercle(B,C,0,pi,affichage=4+rempli); L:=L,triangle(A,B,C,affichage=1+rempli); retourne L; ffonction:;
onload
Solution
On calcule l’aire du triangle :
On calcule la somme des aires de et de :
est obtenue par différence : est égale à la somme des aires des
demi-cercles de diamètres et à laquelle on enlève l’aire en
bleu.
L’aire en bleu est égale à l’aire du demi-cercle de
diamètre à laquelle on enlève l’aire du triangle :
D’après le théorème de Pythagore on a : donc :
.
5.8.2 Exercice 2
Soient un carré de côtés et les cercles ayant comme
diamètre les côtés du carré.
À l’extérieur du carré, ces cercles déterminent avec le cercle
circonscrit au carré 4 lunules.
Trouver l’aire des 4 lunules ainsi déterminées.
À l’intérieur du carré, ces cercles en se coupant déterminent 4
"pétales".
Trouver l’aire des 4 "pétales" ainsi déterminées.
On tape pour faire la figure :
fonction Lunule2() local A,B,C,L; L:=NULL; L:=L,cercle(point(2),2,-pi/2,pi/2,affichage=1+rempli); L:=L,cercle(point(2*i),2,0,pi,affichage=2+rempli); L:=L,cercle(point(-2),2,pi/2,3*pi/2,affichage=3+rempli); L:=L,cercle(point(0,-2),2,pi,2*pi,affichage=4+rempli); L:=L,cercle(0,2*sqrt(2),affichage=5+rempli); L:=L,cercle(point(0,-2),2); L:=L,cercle(point(0,2),2); L:=L,cercle(point(2,0),2); L:=L,cercle(point(-2,0),2); L:=L,carre(-2-2*i,2-2*i); retourne L; ffonction:;
onload
Solution
Un carré est formé de 2 triangles rectangles donc l’aire des 4 lunules est
égale à l’aire du carré (cl le résultat précédent).
La somme de l’aire du carré et de l’aire des "pétales" est égale à
l’aire des 4 demi-cercles de rayon (car les 4 demi-cercles qui sont à
l’intérieur du carré remplissent le carré et se superposent selon les
"pétales") donc l’aire des "pétales"=.
5.8.3 Exercice 3
Si la tentative de la quadrature du cercle est un échec, Hippocrate a trouvé
la quadrature de plusieurs lunules en partant de la remarque suivante :
deux secteurs circulaires et de rayon et semblables
i.e. dont les angles au centre ont la même valeur ont des aires
proportionnelles aux carrés des longueurs de leurs cordes.
On tape pour faire la figure :
fonction Secteurcirc(O,r,t,a) local A,B,L,xO,yO; L:=NULL; [xO,yO]:=coordonnees(O); A:=point(xO+r*cos(t),yO+r*sin(t)); B:=point(xO+r*cos(t+a),yO+r*sin(t+a)); L:=L,cercle(O,r,t,t+a,affichage=4+rempli); L:=L,triangle(O,A,B,affichage=1+rempli); retourne L; ffonction:;
onload
Voici 2 secteurs circulaires identiques :
Voici 2 secteurs circulaires semblables :
Chaque secteur circulaire est composé ici d’un triangle (en rouge) et d’une
calotte circulaire (en bleu). Les aires des triangles ainsi que les aires des
calottes circulaires,sont aussi proportionnelles aux carrés des longueurs des
cordes.
Montrer ces résultats.
Solution
Si deux secteurs circulaires et de rayon et ont pour
angle au centre on a :
La longueur vaut donc
L’aire du secteur circulaire vaut
L’aire du secteur circulaire est donc proportionnelle à .
L’aire du triangle vaut
L’aire du triangle est donc proportionnelle à .
L’aire de la calotte circulaire vaut .
L’aire du calotte circulaire est donc proportionnelle à .
Application
Soit un triangle isocèle et rectangle en .
Soit le symétrique de par rapport à .
Le demi-cercle de diamètre et le quart de cercle de centre
définissent la lunule en rouge sur la figure.
On tape pour faire la figure :
fonction Lunule3() local A,B,C,D,L,xO,yO; L:=NULL; A:=point(-2); B:=point(2,affichage=quadrant1); C:=point([0,2],affichage=quadrant1); D:=point(0,-2); L:=L,affichage(cercle(0,2,0,pi),1+rempli); L:=L,triangle(A,B,C,affichage=4+rempli); L:=L,affichage(cercle(D,2*sqrt(2),pi/4,3*pi/4),6+rempli); L:=L,triangle(A,B,D,affichage=3+rempli); retourne L; ffonction:;
onload
Solution
L’aire des 2 calottes circulaires rouges est égale à l’aire de la calotte
circulaire cyan puisque (car le triangle est
rectangle isocèle de sommet .
L’aire du triangle (aire bleue+aire de la calotte circulaire cyan) est
donc égale à l’aire de la lunule (aires des calottes circulaires
rouges +aire bleue)
5.8.4 Exercice 4
Soit un trapèze isocèle vérifiant et .
Soit l’arc de cercle de centre intersection des médiatrices de
et de (où est un point de l’arc ).
Soit l’arc de cercle tangent à de centre intersection de la
médiatrice de et de la perpendiculaire en à .
On tape pour faire la figure :
fonction Lunule4() local A,B,C,D,I,J,L; L:=NULL; A:=point(-sqrt(3.)); B:=point(sqrt(3.)); C:=point(1,sqrt(2*sqrt(3.))); D:=point(-1,sqrt(2*sqrt(3.))); //I:=inter_unique(droite(x=0),mediatrice(A,D)); I:=point(0,sqrt(-3+2*sqrt(3.))*sqrt(3.))/3); //J:=inter_unique(droite(x=0),perpendiculaire(A,droite(A,C))); J:=point(0,-sqrt(3+2*sqrt(3.))); L:=L,affichage(cercle(I,evalf(longueur(A,I)),angle(I,I+2,B),2*pi+angle(I,I+2,A)),1+rempli); L:=L,triangle(A,B,C,affichage=2+rempli); L:=L,triangle(A,C,D,affichage=2+rempli); L:=L,affichage(cercle(J,sqrt(6+2*sqrt(3.)),angle(J,J+2,B),angle(J,J+2,A)),4+rempli); L:=L,affichage(triangle(J,A,B),3+rempli); L:=L,segment(D,I),segment(C,I),segment(A,C),segment(A,J),segment(J,B),segment(I,J); retourne L; ffonction:;
onload
Solution
L’aire de la lunule (rouge + vert) est égale à l’aire du trapèze
(vert+bleu).
En effet, l’aire de la calotte bleue est égale à 3 fois l’aire d’une calotte
rouge car les secteurs circulaires et sont homothétiques et que
.
On a donc puisque , l’aire des calottes rouges sont égales.
Donc l’aire des 3 calottes rouges est égale à l’aire bleue et :
aire lunule()=aire trapeze()-aire calotte bleue+3*aire lunule()
Donc
aire lunule()=aire trapeze().
Chapitre 6 La géométrie analytique
Dans ce chapitre, les programmes que l’on va faire ne feront pas de tracés
mais renverront des valeurs (coordonnées, coefficients, équations).
On pourra alors faire les figures avec Xcas et vérifier les résultats
obtenus par ces programmes.
6.1 Les segments
6.1.1 Calculer la distance de deux points connaissant leurs coordonnées
Si les points A et B ont pour coordonnées cA:=[xA,yA] et
cB:=[xB,yB] le segment AB a pour longueur :
sqrt((xA-xB)^
2+(yA-yB)^
2)
On tape :
fonction Longueur(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne simplify(sqrt((xA-xB)^2+(yA-yB)^2)); ffonction:;
onload
Vérifions avec Xcas :
6.1.2 Calculer les coordonnées du milieu d’un segment
Si les points A et B ont pour coordonnées cA:=[xA,yA] et
cB:=[xB,yB], le milieu de AB a pour coordonnées
[(xA+xB)/2,(yA+yB)/2] :
On tape :
fonction Milieu(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne [(xA+xB)/2,(yA+yB)/2]; ffonction:;
onload
Vérifions avec Xcas :
6.2 Les droites
6.2.1 Équation d’une droite définie par 2 points ou par sa pente et un point
Équation d’une droite définie par 2 points
Si les points A et B ont pour coordonnées cA:=[xA,yA] et
cB:=[xB,yB], la droite d passant par A et B a pour
équation :
(xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB=0 ou encore
si (xA==xB) alors l’équation de d est x=xA
si (xA!=xB) alors l’équation de d est
y=(yA-yB)*(x-xB)/(xA-xB)+yB
Équation d’une droite définie par sa pente et un point
La droite passant par le point A de coordonnées
cA:=[xA,yA] et de pente m a pour équation :
y=m*(x-xA)+yA.
On tape :
fonction Droite1(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne normal((xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB)=0; ffonction:; fonction Droite2(cA,m) local xA,yA; xA:=cA[0]; yA:=cA[1]; retourne y-m*(x-xA)-yA=0; ffonction:;
On peut réunir les 2 programmes en un seul en testant la dimension du
deuxième paramètre de Droite qui est soit une liste de dimension 2,
soit un réél.
On peut aussi faire en sorte que Droite accepte 1 seul argument qui soit
son équation : pour cela, on donne au deuxième argument une valeur par
défaut (ici cette valeur sera arbitraire par exemple 0) (cf 2.2.4).
On tape :
fonction Droite(cA,L=0) local xA,xB,yA,yB,m; si type(cA)==expression alors retourne cA;fsi; xA:=cA[0]; yA:=cA[1]; si type(L)==vecteur alors xB:=L[0]; yB:=L[1]; retourne normal((xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB)=0; sinon m:=L; retourne y-m*(x-xA)-yA=0; fsi; ffonction:;
onload
Observez qu’on a donné une valeur par défaut 0
au deuxième paramètre L
, si Droite
est appelée
avec deux arguments tout se passe comme si on avait écrit
L
et non L=0
, par contre si Droite
est
appelée avec un seul argument, alors L
prend la valeur
0 au début de la fonction.
Vérifions avec Xcas :
6.2.2 Coefficients (a,b,c) de la droite d’équation ax+by+c=0
Étant donnée l’équation d’une droite a*x+b*y+c=0, on va écrire une
fonction qui renvoie les coefficients a, b et c.
On utilise tout d’abord gauche et droit qui renvoie le côté
gauche et le côté droit d’une équation.
Par exemple si Eq:=eq1=eq2 alors
gauche(Eq) renvoie eq1 et
droit(Eq) renvoie eq2 donc
gauche(Eq)-droit(Eq) renvoie eq
qui est égal à eq1-eq2.
On peut alors trouver a, b et c en donnant des valeurs à
x et y.
Posons :
c:=subst(eq,[x,y],[0,0])
d1:=subst(eq,[x,y],[1,0])
d2:=subst(eq,[x,y],[0,1])
Alors on a a:=d1-c et b:=d2-c
On tape :
fonction Coeffsdroite(Eq) local a,b,c,d1,d2,eq; eq:=gauche(Eq)-droit(Eq); c:=subst(eq,[x,y],[0,0]); d1:=subst(eq,[x,y],[1,0]); d2:=subst(eq,[x,y],[0,1]); retourne normal(d1-c,d2-c,c); ffonction:;
onload
Remarque
On peut aussi utiliser :
coeff(P(x,y),x) (resp coeff(P(x,y),y)) qui renvoie la liste
des coefficients selon les puissances décroissantes du polynôme P par
rapport à la variable x (resp y) et
coeff(P(x,y),x,n) (resp coeff(P(x,y),y,n)) qui renvoie le
coefficient de x^
n (resp de y^
n) du
polynôme P.
On tape :
fonction Coeffdroite(Eq) local a,b,c; Eq:=gauche(Eq)-droit(Eq); a:=coeff(Eq,x,1); b:=coeff(Eq,y,1); c:=subst(Eq,[x,y]=[0,0]); retourne normal(a,b,c); ffonction:;
Vérifions avec Xcas :
6.2.3 Point d’intersection de 2 droites sécantes
Cette section reprend la section sur la résolution de système
de 2 équations à 2 inconnues.
Soient deux droites d1 et d2 d’équation :
et
Ces 2 droites sont parallèles si .
Si d1 et d2 sont sécantes.
Les coordonnées de leur point d’intersection sont
Interdroite(d1,d2) renvoie [] si d1 et d2 sont
paralléles et sinon renvoie les coordonées de leur point d’intersection.
On utilise les programmes Droite et Coeffsdroite précédents
(cf 6.2.2 pour avoir les coefficients
des équations Eq1 et Eq2 de d1 et de d2
On tape :
fonction Interdroite(d1,d2) local a1,a2,b1,b2,c1,c2,gd1,gd2,d; (a1,b1,c1):=Coeffsdroite(d1); (a2,b2,c2):=Coeffsdroite(d2); d:=normal(a2*b1-b2*a1); si d==0 alors retourne [];fsi; retourne [(b2*c1-b1*c2)/d,(c2*a1-a2*c1)/d]; ffonction:;
On fait la figure avec Xcas:
Vérifions avec Xcas :
6.3 Triangles et quadrilatères définis par les coordonnées des sommets
On définit des versions de la commande polygone
de Xcas donc ces 2 programmes vont faire des figures.
fonction Triangle(cA,cB,cC) retourne polygone(cA,cB,cC); ffonction:; fonction Quadrilatere(cA,cB,cC,cD) retourne polygone(cA,cB,cC,cD); ffonction:;
6.4 Les vecteurs
6.4.1 Les coordonnées d’un vecteur défini par 2 points
Si les coordonnées du point A (resp B) sont cA:=[xA,yA]
(resp cB:=[xB,yB]), les coordonnées du vecteur AB
sont [xB-xA,yB-yA].
On tape :
fonction Vecteur(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne normal([xB-xA,yB-yA]); ffonction:;
ou plus simplement :
On fait la figure avec Xcas :
Vérifions avec Xcas :
6.4.2 Calculer les coordonnées de la somme de deux vecteurs dans un repère
Si les vecteurs V1 et V2 ont pour coordonnées cV1:=[x1,y1]
et cV2:=[x2,y2], les coordonnées du vecteur V1+V2 sont
[x1+x2,y1+y2].
On tape :
fonction SumVect(cV1,cV2) local x1,x2,y1,y2; x1:=cV1[0]; y1:=cV1[1]; x2:=cV2[0]; y2:=cV2[1]; retourne normal([x1+x2,y1+y2]); ffonction:;
ou plus simplement :
On fait la figure avec Xcas :
Vérifions avec Xcas :
6.4.3 Coordonnées de extrémité du vecteur d’origine équipollent au vecteur
On a .
On tape :
On fait la figure avec Xcas :
Vérifions avec Xcas :
6.4.4 Norme d’un vecteur
Soit le vecteur V:=[xV,yV], on pose cV:=[xV,yV] la liste des
coordonnées de V.
La norme de V est égale à sqrt(xV^
2+yV^
2).
On tape :
fonction Norme(cV) local xV,yV; xV:=cV[0]; yV:=cV[1]; retourne sqrt(xV^2+yV^2); ffonction:;
Vérifions avec Xcas :
6.5 Changement de repères
6.5.1 Le problème
Soient 2 repères orthonormés et .
Notations :
-
Soit un point ou un vecteur.
On note les coordonnées de dans le repère .
On note les coordonnées de dans le repère . - On note (resp ) le vecteur unitaire porté par (resp )
- On note (resp ) le vecteur unitaire porté par (resp )
Avec ces notations, on a :
- le point a pour coordonnées dans le repère donc
- le vecteur a pour coordonnées dans le repère () donc
- le vecteur a pour coordonnées dans le repère () donc . L’angle donc et
6.5.2 Le programme Changexy2XY(cM,cI,cU)
On connaît les coordonnées d’un point dans le
repère ainsi que les coordonnées et
de et de dans le repère .
On cherche les coordonnées de
dans le repère .
On a donc :
Donc :
on en déduit
Or et donc
Finalement :
On tape :
fonction Changexy2XY(cM,cI,cU) local xM,xI,xU,xV,yM,yI,yU,yV,l; xM:=cM[0]; yM:=cM[1]; xI:=cI[0]; yI:=cI[1]; xU:=cU[0]; yU:=cU[1]; l:=xU^2+yU^2; si l!=1 alors l:=sqrt(l);xU:=xU/l;yU:=yU/l;fsi; xV:=-yU; yV:=xU; retourne normal([((xM-xI)*xU+(yM-yI)*yU),(-(xM-xI)*yU+(yM-yI)*xU)]); ffonction:;
Remarque
Dans le programme ci-dessus, on teste si le vecteur U est unitaire, si ce
n’est pas le cas, on le rend unitaire avec :
l:=xU^
2+yU^
2;si l!=1 alors l:=sqrt(l);xU:=xU/l;yU:=yU/l;fsi;
On tape :
La figure avec Xcas :
6.5.3 Le programme ChangeXY2xy(CM,cI,cU)
Il s’agit du programme inverse du précédent : on connaît les coordonnées d’un point dans le repère ainsi que les coordonnées et de et de dans le repère . On cherche les coordonnées de dans le repère .
On a vu précédemment que : On tape :
fonction ChangeXY2xy(CM,cI,cU) local XM,xI,xU,xV,YM,yI,yU,yV,l; XM:=CM[0]; YM:=CM[1]; xI:=cI[0]; yI:=cI[1]; xU:=cU[0]; yU:=cU[1]; l:=xU^2+yU^2; si l!=1 alors l:=sqrt(l);xU:=xU/l;yU:=yU/l;fsi; xV:=-yU; yV:=xU; retourne normal([xI+XM*xU+YM*xV,yI+XM*yU+YM*yV]); ffonction:;
onload
Remarque
Dans le programme ci-dessus, si le vecteur U n’est pas
unitaire, on le rend unitaire :
l:=xU^
2+yU^
2;si l!=1 alors l:=sqrt(l);xU:=xU/l;yU:=yU/l;fsi;
On tape :
La figure avec Xcas :
6.5.4 Exercices
En se servant des programmes précédents, faire les programmes :
- calculant les coordonnées du sommet d’un triangle équilatéral direct connaissant les coordonnées de et de ,
- calculant les coordonnées et des sommets et d’un carré direct connaissant les coordonnées de et de .
Solution
1/
Soit un triangle équilatéral direct . Dans le repère
orthonormé , a pour coordonnées et a pour
coordonnées .
Cherchons les coordonnées du point dans le
repère d’origine et d’axe des dirigé selon le vecteur .
On pose :
On a :
Et les coordonnées de dans le repère orthonormé sont :
ChangeXY2xy(CC,cA,cU) (fonction écrite précédemment cf
6.5.3).
On tape :
fonction Coordequi(cA,cB) local xA,yA,xB,yB,cU,l,CC; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; l:=sqrt((xB-xA)^2+(yB-yA)^2); cU:=[(xB-xA)/l,(yB-yA)/l]; CC:=[l/2,l*sqrt(3)/2]; retourne ChangeXY2xy(CC,cA,cU); ffonction:;
On fait la figure avec Xcas :
Vérifions avec Xcas :
2/
Soit un carré direct , avec de coordonnées
et de coordonnées
dans le repère
orthonormé .
Cherchons les coordonnées
du point et
des points et dans le repère d’origine et d’axe des dirigé
selon le vecteur . On pose :
On a :
Donc les coordonnées de dans le repère orthonormé sont :
ChangeXY2xy(CC,cA,cU)
les coordonnées de dans le repère orthonormé sont :
ChangeXY2xy(CD,cA,cU) (fonction écrite précédemment cf
6.5.3).
fonction Coordcarre(cA,cB) local xA,yA,xB,yB,CC,CD,cU,l; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; l:=sqrt((xB-xA)^2+(yB-yA)^2); cU:=[(xB-xA)/l,(yB-yA)/l]; CC:=[l,l]; CD:=[0,l]; retourne ChangeXY2xy(CC,cA,cU),ChangeXY2xy(CD,cA,cU); ffonction:;
On fait la figure avec Xcas :
On vérifie avec Xcas :
6.6 Cercles, Tangentes à un cercle
6.6.1 Équation d’un cercle défini par son centre et son rayon
Le cercle défini par son centre de coordonnées
et de rayon a pour équation :
ou encore
On va écrire une procédure Cercle qui renvoie une liste constituée des
coordonnées de son centre, de son rayon et de son équation.
On tape :
fonction Cercle1(cA,r) //cercle defini par son centre et son rayon local xA,yA; xA:=cA[0]; yA:=cA[1]; retourne [cA,r,x^2+y^2-2*xA*x-2*yA*y+xA^2+yA^2-r^2=0]; ffonction:;
6.6.2 Équation d’un cercle défini par son diamètre
Si les points et ont pour coordonnées et
, le cercle de diamètre a pour équation :
si M:= Milieu(A,B), si xM:=M[0], si yM:=M[1] et si
r:=Longueur(A,B)/2 :
(x-xM)^
2+(y-yM)^
2=r^
2 ou encore
x^
2+y^
2-2*xM*x-2*yM*y+xM^
2+yM^
2-r^
2=0
On va écrire une procédure Cercle qui renvoie une liste constituée des
coordonnées de son centre, de son rayon et de son équation.
On tape :
fonction Cercle2(cA,cB) //cercle d\'efini par son diam\`etre local xA,xB,xM,yA,yB,yM,M,r; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; cM:=Milieu(cA,cB): xM:=cM[0]; yM:=cM[1]; r:=Longueur(cA,cB): retourne [cM,r,x^2+y^2-2*xM*x-2*yM*y+xM^2+yM^2-r^2=0]; ffonction:;
6.6.3 Équation d’un cercle défini par son centre et son rayon ou par son diamètre
On peut reunir les 2 programmes en un seul en testant la dimension du
deuxième paramètre de Cercle qui est soit
une liste de dimension 2 (cercle défini par son diamètre),
soit un réel (cercle défini par centre et rayon).
On tape :
fonction Cercle(cA,L) local cB,xA,xB,xM,yA,yB,yM,cM,r; xA:=cA[0]; yA:=cA[1]; si type(L)==vecteur alors xB:=L[0]; yB:=L[1]; cB:=[xB,yB] cM:=Milieu(cA,cB); xM:=cM[0]; yM:=cM[1]; r:=Longueur(cA,cB)/2; retourne [cM,r,x^2+y^2-2*xM*x-2*yM*y+xM^2+yM^2-r^2=0]; sinon r:=L; retourne [cA,r,x^2+y^2-2*xA*x-2*yA*y+xA^2+yA^2-r^2=0]; fsi; ffonction:;
onload
On fait la figure avec Xcas :
On vérifie avec Xcas :
6.6.4 Centre et rayon d’un cercle donné par son équation
On utilise ici les commandes Xcas
gauche, droit ,coeff, subst
On tape :
fonction Centrerayon(Eq) local k,a,b,c; Eq:=gauche(Eq)-droit(Eq); k:=coeff(Eq,x,2); si k!=coeff(Eq,y,2) alors retourne "ce n'est pas un cercle";fsi; Eq:=Eq/k; a:=-coeff(Eq,x,1)/2; b:=-coeff(Eq,y,1)/2; c:=subst(Eq,[x,y],[0,0]); retourne [a,b], normal(sqrt(a^2+b^2-c)); ffonction:;
On vérifie avec Xcas :
6.6.5 Construire la tangente à un cercle en l’un de ses points
Soit un cercle de centre de coordonnées
et de rayon .
Soit un point de de coordonnées .
la tangente au cercle en est perpendiculaire à , donc
a pour pente
L’équation de cette tangente est donc : Droite(cA,m) (fonction
Droite a été écrite précédemment cf 6.2.1).
On utilise aussi, ici, les fonctions Longueur (cf 6.1.1) et
Cercle (cf 6.6.3).
On tape :
fonction Tangent1(C,cA) local I,r,m,xI,yI,xA,yA,cI; cI:=C[0]; r:=C[1]; si Longueur(cA,cI)!=r alors retourne "A n'est pas sur C"; fsi; xI:=cI[0]; yI:=cI[1]; xA:=cA[0]; yA:=cA[1]; si yA!=yI alors m:=-(xA-xI)/(yA-yI); retourne Droite(cA,m); fsi retourne x=xA; ffonction:;
On fait la figure avec Xcas :
On vérifie avec Xcas :
6.6.6 Construire les tangentes à un cercle passant par un point
Soit un cercle de centre de coordonnées
et de rayon .
Soit un point du plan de coordonnées .
Si est à l’intérieur de il n’y a pas de tangente à
passant par .
Le cas simple
On suppose qu’on a choisi comme repère, le repère
d’origine le centre du cercle et
tel que est sur l’axe des (i.e. de coordonnées ) et
à l’extérieur de .
On peut mener par , 2 tangentes et à .
Soient et les 2 points de tangeance de et .
Les triangles (resp ) sont rectangles en
(resp ) et est perpendiculaire à .
Soit l’intersection de avec .
On fait la figure avec Xcas :
On a :
Donc dans le repère ,
a pour abscisse et pour ordonnée 0.
a pour abscisse et pour ordonnée 0.
et ont la même abscisse :
et ont des ordonnées opposées :
Les tangentes sont donc :
Si est sur (i.e. ) alors on peut mener par , une tangente à qui est .
Le cas général
On se ramène au cas précédent par changement de repère.
Soit le repère.
Dans le repère , (resp ) a pour coordonnées (resp ).
On fait un changement de repère en prenant le centre du cercle
comme origine et comme axe des .
On note et les coordonnées d’un point ou d’un
vecteur dans le nouveau repère et
et les coordonnées de dans le repère .
Dans le repère , a pour abscisse et
pour ordonnée 0.
a pour abscisse et pour ordonnée 0.
Si est le vecteur unitaire de on a :
et avec .
On a vu que (cf 6.5.2) :
Donc :
En remplaçant et par leur valeur, on a :
Dans le repère les coordonnées de et sont :
Donc :
On écrit la fonction Tangent(C,cA) qui renvoie une liste
(éventuellement vide) contenant la ou les équations des tangentes au cercle
C passant par le point de coordonnées cA, en utilisant
Milieu (cf 6.1.2), Longueur (cf 6.1.1),
Droite(cf 6.2.1) et Cercle (cf 6.6.3).
On tape :
fonction Tangent(C,cA) local cI,r,xI,yI,xA,yA,XM1,YM1,xM1,yM1,XM2,YM2,xM2,yM2,l; cI:=C[0]; xI:=cI[0]; yI:=cI[1]; r:=C[1]; l:=Longueur(cA,cI); xA:=cA[0]; yA:=cA[1]; si l < r alors print("A n'est pas a l'exterieur de C"); retourne []; fsi; si l==r et yA==yI alors retourne [x-xA=0]; fsi; si l==r et (yA-yI)!=0 alors retourne [normal(Droite([xA,yA],-(xA-xI)/(yA-yI)))]; fsi; XM1:=r^2/l; YM1:=r*sqrt(1-r^2/l^2); xM1:=normal(xI+(XM1*(xA-xI)-YM1*(yA-yI))/l); yM1:=normal(yI+(XM1*(yA-yI)+YM1*(xA-xI))/l); XM2:=r^2/l; YM2:=-r*sqrt(1-r^2/l^2); xM2:=normal(xI+(XM2*(xA-xI)-YM2*(yA-yI))/l); yM2:=normal(yI+(XM2*(yA-yI)+YM2*(xA-xI))/l); retourne [Droite([xM1,yM1],[xA,yA]),Droite([xM2,yM2],[xA,yA])]; ffonction:;
On fait la figure avec Xcas :
On fait la figure avec Xcas :
On vérifie avec Xcas :
On peut utiliser non seulement Milieu (cf 6.1.2),
Longueur (cf 6.1.1), Droite(cf 6.2.1) et
Cercle (cf 6.6.3), mais aussi le programme de changement de
repère écrit précedemment ChangeXY2xy (cf 6.5.3 :
fonction Tangentes(C,cA) local cI,r,xI,yI,xA,yA,XA,CM1,CM2,cM1,cM2,cU; cI,r:=C; xI,yI:=cI; xA,yA:=cA; XA:=Longueur(cA,cI); cU:=[xA-xI,yA-yI]/XA; si XA >r alors CM1:=[r^2/XA,r*sqrt(1-r^2/XA^2)]; CM2:=[r^2/XA,-r*sqrt(1-r^2/XA^2)]; cM1:=ChangeXY2xy(CM1,cI,cU); cM2:=ChangeXY2xy(CM2,cI,cU); retourne [Droite(cM1,[xA,yA]),Droite(cM2,[xA,yA])]; fsi; si XA==r alors CM1:=[r,1]; cM1:=ChangeXY2xy(CM1,cI,cU); retourne [Droite(cM1,[xA,yA])]; fsi retourne []; ffonction:;
On vérfie avec Xcas :
6.6.7 Solution analytique des tangentes à un cercle
On peut aussi faire une résolution analytique pour construire la (ou les)
tangente(s) à un cercle passant par un point .
On pourra se servir des programmes écrits précédement : Longueur
(cf 6.1.1), Droite(cf 6.2.1), Cercle
(cf 6.6.3) et Solution12 (cf 4.2).
On considère un repère orthonormé dans lequel le centre du cercle
de rayon a pour coordonnées .
a pour équation soit
Soit un point de coordonnées .
Si se trouve à l’extérieur du cercle , on peut mener par
, deux tangentes. Les points de contact et de ces
tangentes avec sont aussi les points d’intersection de et du
cercle de diamètre .
Le cercle de diamètre a pour centre et rayon
où est le milieu de
de coordonnées =Milieu et
Longueur.
Ce cercle a donc comme équation i.e.
Il faut donc résoudre le système d’inconnues
qui est équivalent à :
On a et
Or donc ou .
Si (resp ) alors on connaît
(resp ) en fonction
de (resp ) et il faut résoudre une équation
de degré 2 en (res ).
Si est sur le cercle , on peut mener par
, une tangente
(c’est une droite passant par et qui est perpendiculaire à .
Si est à l’intérieur du cercle , il n’y a pas de
tangente passant par .
On utilise encore Longueur (cf 6.1.1), Droite
(cf 6.2.1) et Cercle (cf 6.6.3), mais aussi le
programme Solution12 écrit précedemment (cf 4.2 :
On tape :
fonction Tangenteq(C,cA) local cI,r,l,xI,yI,xA,yA,xK,yK,Eq,xM,yM,xM1,yM1,xM2,yM2,R,m; cI:=C[0]; xI:=cI[0]; yI:=cI[1]; r:=C[1]; l:=Longueur(cA,cI); xA:=cA[0]; yA:=cA[1]; R:=l/2; si l >r alors xK:=(xI+xA)/2; yK:=(yI+yA)/2; si (yI-yA)!=0 alors yM:=(xK^2+yK^2+r^2-xI^2-yI^2-R^2+2*(xI-xK)*x)/(yA-yI); Eq:=x^2+yM^2-2xI*x-2yI*yM+xI^2+yI^2-r^2=0; [xM1,xM2]:=Solution12(Eq,x); yM1:=subst(yM,x=xM1); yM2:=subst(yM,x=xM2); sinon xM:=(xK^2+yK^2+r^2-xI^2-yI^2-R^2+2*(yI-yK)*y)/(xA-xI); Eq:=xM^2+y^2-2xI*xM-2yI*y+xI^2+yI^2-r^2=0; [yM1,yM2]:=Solution12(Eq,y) xM1:=subst(xM,y=yM1); xM2:=subst(xM,y=yM2); fsi; retourne [Droite(cA,[xM1,yM1]),Droite(cA,[xM2,yM2])]; fsi; si l==r alors si (yI-yA)!=0 alors m:=normal(-(xA-xI)/(yA-yI)); retourne [Droite([xA,yA],m)]; sinon retourne [Droite([xA,yA],[xA,yA+1])]; fsi; fsi; retourne []; ffonction:;
On vérifie avec Xcas :
Chapitre 7 Quelques tests géométriques
7.1 Test d’alignement de 3 points
Établir que trois points sont alignés ou non alignés.
Si les 3 points sont confondus Estaligne(A,B,C) renvoie 2
Si les 3 points sont alignés Estaligne(A,B,C) renvoie 1
Si les 3 points ne sont pas alignés Estaligne(A,B,C) renvoie 0
On tape :
fonction Estaligne(cA,cB,cC) local xA,yA,xB,yB,xC,yC; si cA==cB et cA==cC alors retourne 2; fsi; si cA==cB ou cA==cC alors retourne 1; fsi; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; xC:=cC[0]; yC:=cC[1]; si normal((xB-xA)*(yC-yA)-(xC-xA)*(yB-yA))==0 alors retourne 1; fsi; retourne 0; ffonction:;
On fait la figure avec Xcas :
On vérifie avec Xcas :
7.2 Test de parallélisme de 2 droites
Soient deux droites et d’équation :
et
Ces 2 droites sont parallèles si .
On utilise la fonction Coeffsdroite (cf 6.2.2 qui calcule
les coefficients a, b et c de l’équation d’une droite
ax+by+c=0 :
On tape :
fonction Estparallele(d1,d2) local a1,a2,b1,b2,d,c1,c2; a1,b1,c1:=Coeffsdroite(d1); a2,b2,c2:=Coeffsdroite(d2); d:=normal(a2*b1-b2*a1); si d==0 alors retourne 1; fsi; retourne 0; ffonction:;
On fait la figure avec Xcas :
On vérifie avec Xcas :
7.3 Caractériser alignement et parallélisme par la colinéarité
Trois points , et sont alignés si les vecteurs
et sont colinéaires.
Deux droites et sont parallèles si les vecteurs et
sont colinéaires.
Chapitre 8 Statistiques
8.1 Calcul de moyenne et écart-type
Exercice : Écrire une fonction prenant en argument la liste des
données et renvoyant sa moyenne et son écart-type, au
moyen d’une boucle pour
(sans utiliser mean
ou stddev
qui
sont les commandes Xcas pour moyenne et écart-type).
fonction Stats(L) local j,Lj,n,s,s2; n:=dim(L); s:=0; s2:=0; pour j de 0 jusque n-1 faire Lj := L[j]; s := s+Lj s2 := s2+Lj^2; fpour retourne s/n,sqrt(s2/n-(s/n)^2); ffonction:;
On vérifie avec Xcas :
Exercice : Écrire une fonction prenant en argument la liste des
données et renvoyant sa médiane.
Indication : Pour déterminer une médiane, il faut au préalable
trier les données, ce qui est la partie difficile de l’algorithme, le reste
de l’algorithme est simple. Au niveau du lycée on peut
utiliser la commande Xcas sort
qui trie une liste par ordre
croissant, puis on renvoie l’élément d’indice la taille de la liste/2.
fonction Median(L) local n; n:=dim(L); L:=sort(L); si irem(n,2)==1 alors retourne L[(n-1)/2]; sinon retourne (L[n/2-1]+ L[n/2])/2.; fsi; ffonction:;
On vérifie avec Xcas :
Exercice : Écrire une fonction qui calcule la moyenne et l’écart-type
et qui a comme argument soit la liste alternant valeur et effectif soit
2 listes.
fonction Statexo1(L) local j,lj,n,n1,s,s2,vj,ej; n:=dim(L); si type(n)!=vecteur alors retourne "erreur"; fsi; s:=0; s2:=0; n1:=n[0]; n:=sum(col(L,1)); pour j de 0 jusque n1-1 faire lj := L[j]; vj := lj[0]; ej := lj[1]; s := s+vj*ej s2 := s2+vj^2*ej; fpour; retourne s/n,sqrt(s2/n-(s/n)^2); ffonction:; fonction Statexo2(L1,L2) local j,vj,ej,n,n1,n2,s,s2; n1:=dim(L1); n2:=dim(L2); si n1!=n2 alors retourne "erreur"; fsi; n:=sum(L2); s:=0; s2:=0; pour j de 0 jusque n1-1 faire vj := L1[j]; ej := L2[j]; s := s+vj*ej s2 := s2+vj^2*ej; fpour; retourne s/n,sqrt(s2/n-(s/n)^2); ffonction:;
On a mesuré la taille en cm (arrondie à l’entier le plus proche) de 200
fossiles de la même espèce. On a obtenu pour k=0..12 une taille
L1[k] d’effectif de L2[k].
Calculer la moyenne et l’écart-type de cette distribution.
On a mesuré la taille en cm (arrondie à l’entier le plus proche) d’une autre
espèce de fossiles. On a obtenu pour k=0..12 une taille
L1[k] d’effectif de L3[k].
Calculer la moyenne et l’écart-type de cette distribution.
On vérifie avec Xcas :
8.2 Simulation d’un échantillon
Exercice : Générer aléatoirement valeurs 0 ou 1,
la probabilité d’avoir 1 étant fixée à
(pour faire cela, on comparera avec le résultat de la commande Xcas
alea(0,1)
qui renvoie un réel entre 0 et 1).
Calculer la fréquence de 1 observée.
fonction Simu(n,p) local j,a,n1; n1:=0; pour j de 1 jusque n faire a:=alea(0,1); si a<=p alors n1:=n1+1; fsi; fpour retourne n1/n; ffonction:;
onload
On peut visualiser les fréquences obtenues avec la commande
plotlist([y0,...,yn]) de Xcas qui trace la ligne polygonale reliant
les points d’abscisse k et d’ordonnée yk pour k=0..n.
Commandes Xcas permettant de faire le calcul directement :
La commande randvector
génère ici valeurs aléatoires
selon la loi binomiale de paramètres 1 et , on en fait ensuite
la moyenne. On peut bien sur utiliser d’autres lois que la loi binomiale,
par exemple la loi uniforme (uniform
), la loi normale
(normald
), la loi exponentielle (exponential
), ...
8.3 Intervalle de fluctuation
Écrire un algorithme effectuant simulations d’échantillons, en utilisant la fonction précédente, renvoyer la liste des fréquences ainsi que la proportion de fréquences situées en-dehors de l’intervalle .
Rappelons qu’on s’intéresse à cet intervalle, parce qu’une simulation suit une loi binomiale de moyenne et d’écart-type donc les fréquences suivent une loi binomiale de moyenne et d’écart-type . Or donc Pour grand, la loi binomiale s’approche d’une loi normale ayant cet écart type.
On utilise la fonction Simu écrite précédemment (cf 8.2).
fonction Fluctuat(N,n,p) local j,L,out; L:=[]; out:=0; pour j de 0 jusque N-1 faire L[j]:=Simu(n,p); si abs(L[j]-p)>1/sqrt(n) alors out:=out+1; fsi; fpour; retourne out/N,L; ffonction:;
On lance 20 fois une pièce de de monnaie mal équilibrée, la
probabilité d’obtenir face étant égale à 0.4.
Simu(20,0.4) renvoie donc la fréquence du nombre de faces observées.
On effectue plusieurs fois 100 simulations :
On peut visualiser la répartition de ces fréquences :
Avec les commandes de Xcas
On génère une séquence de moyennes de vecteurs de
valeurs aléatoires selon la loi binomiale de paramètres 1 et :
On compte les moyennes dont l’écart à est plus grand que :
On peut accélérer le calcul en utilisant le générateur
aléatoire selon la loi binomiale
de paramètres et
On peut enfin tracer le graphe de l’évolution de la
proportion de fréquences en-dehors
de l’intervalle lorsque augmente.
On génère une liste de fréquences :
puis on compte les fréquences en-dehors de l’intervalle et on
stocke les fréquences dans une liste
La probabilité théorique d’être dans l’intervalle
est donnée par la commande
Xcas :
Il y a donc une probabilité faible (de l’ordre de 4%)
de ne pas être dans l’intervalle de fluctuation.
La probabilité d’être dans l’intervalle plus précis
est donnée par :
Elle est ici identique parce que les bornes de l’intervalle ont la même
partie entière.
8.4 Évolution d’une fréquence.
On illustre l’évolution d’une fréquence vers sa moyenne théorique (espérance).
Expérience : on tire un dé à 6 faces, si le résultat est 1 on
renvoie 1, si c’est 2 à 4 on renvoie 2, si c’est 5 ou 6 on renvoie 4.
Simulation, en utilisant la fonction piecewise
:
fonction experience() local a; a:=alea(6)+1; // simule un de 6 return piecewise(a<2,1,a<5,2,4); ffonction:;
onload
On effectue expériences et on regarde l’évolution de la moyenne des résultats.
fonction evolutionmoyenne1(N) local l,s,j,a; l:=[]; s:=0; pour j de 1 jusque N faire s:=s+experience(); l:=append(l,s/j); fpour; return l; ffonction:;
onload
Dans la version HTML interactive,
cliquez plusieurs fois sur le bouton ok pour voir différents
comportements.
On peut écrire une fonction plus générale en passant
la fonction experience
en argument
fonction evolutionmoyenne(experience,N) local l,s,j,a; l:=[]; s:=0; pour j de 1 jusque N faire s:=s+experience(); l:=append(l,s/j); fpour; return l; ffonction:;
onload
8.5 Triangles de spaghettis
On prend un spaghetti qu’on casse en 3 morceaux, peut-on construire un triangle avec ces 3 morceaux ? On fixe l’unité de longueur au spaghetti entier. On a donc 3 longueurs , et et il faut vérifier les inégalités triangulaires (voir la section 9.4).
Première méthode de coupe : on tire au hasard et entre 0 et 1.
Si , on pose (et donc ) sinon on échange et
(, et ).
Simulation, correct
vérifie l’inégalité triangulaire,
spag1
renvoie 1 si on peut faire un triangle et 0 sinon :
correct(a,b,c):=a<b+c et b<a+c et c<a+b:; fonction spag1() local x,y,c; x:=alea(0,1); y:=alea(0,1); si y>x alors return correct(x,y-x,1-y); sinon return correct(y,x-y,1-x); fsi; ffonction:; fonction spagn(n) local L,s; L:=[]; // liste des frequences de succes s:=0; // nombre de succes pour j de 1 jusque n faire si spag1() alors s++ fsi; L:=append(L,s/j); fpour; return L; ffonction:;
onload
Exercice : modifier spag1
pour
couper le spaghetti en prenant au hasard, puis en coupant le
morceau le plus long au hasard.
fonction spag1() local x,y,c; x:=alea(0,1); si x>1/2 alors y:=x; // on casse x x:=x*alea(0,1); sinon // on casse 1-x y:=x+(1-x)*alea(0,1); fsi; return correct(x,y-x,1-y); ffonction:;
Voir l’étude à la fin du manuel Algorithmes
et simulation de Xcas.
8.6 Les aiguilles de Buffon
Le naturaliste Buffon, en 1777 a posé le problème de l’aiguille en ces termes : "Je suppose que dans une chambre, dont le parquet est simplement divisé par des joints parallèles, on jette en l’air une baguette et que l’un des joueurs parie que la baguette ne croisera aucune des parallèles du parquet..."
On peut modéliser le problème en supposant que la distance entre les paralléles est de 1, et que les parallèles sont horizontales, d’équations . On suppose que la longueur de l’aiguille est . Lorsqu’elle tombe sur le parquet, on suppose qu’une de ses extrémités est en et qu’elle fait un angle avec l’horizontale, son autre extrémité est donc en . Elle coupe une des parallèle si l’intervalle contient un entier c’est-à-dire si la partie entière de diffère de celle de . On observe que la valeur de n’a pas d’importance, et que la valeur de modulo 1 non plus. Pour effectuer la simulation, on suppose donc que est un réel aléatoire selon la loi uniforme entre 0 et 1, et un réel aléatoire selon la loi uniforme entre et
fonction buffonpi(l) local y,theta; y:=alea(0,1); theta:=alea(-.5,.5)*pi; si floor(y+l*sin(theta))!=0 alors return 1; sinon return 0; fsi; ffonction:;
onload
Tests pour quelques valeurs de
On peut aussi générer la direction aléatoire sans utiliser le nombre en tirant au hasard deux réels entre 0 et 1 et si le point obtenu est dans le disque unité, on prend la direction obtenue.
fonction buffonsanspi(l) local y,X,Y; y:=alea(0,1); repeter X:=alea(0,1); Y:=alea(0,1) jusqua X*X+Y*Y<=1; si floor(y+l*Y/sqrt(X^2+Y^2))!=0 alors return 1; sinon return 0; fsi; ffonction:;
onload
On peut enfin définir algébriquement
Pour conjecturer le comportement de la probabilité en fonction de on va l’estimer par la fréquence observée au bout de d’expériences, par exemple on prendra (pour éviter un temps d’exécution trop long)
fonction probabuffon(l,N) local s,j; s:=0; pour j de 1 jusque N faire s := s+buffonpi(l); fpour; return s/N; ffonction:;
onload
Pour petit, le comportement est linéaire, pour grand
on observe une asymptote horizontale (qui est car lorsque
la taille de l’aiguille est de plus en plus grande,
la probabilité de ne pas couper une horizontale tend vers 0
puisque la direction doit être de plus en plus proche).
On peut démontrer que
le coefficient de proportionnalité pour petit vaut ,
(cf. le manuel Exercices de Xcas pour ).
Pour obtenir un tracé plus précis sans attendre trop longtemps,
il faut trouver une astuce pour accélérer le calcul dans
probabuffon
.
Pour cela on part de la version algébrique de buffon
qui
se prête au calcul appliqué en parallèle à une liste
(sin
appliqué à une liste renvoie la liste des sinus
des éléments de la liste, +
effectue la somme composante
par composante, etc.).
On doit renvoyer 0 si
le nombre de parallèles traversées
est nul et 1 sinon, donc si on fait expériences,
la proportion est donnée par :
On peut maintenant tracer le graphe de manière plus précise,
par exemple avec et un pas de 0.03 sur , sans patienter
trop longtemps :
Ce type d’optimisation est fréquent dans les langages interprétés,
on essaie d’utiliser les commandes natives du logiciel pour effectuer
les boucles les plus internes au lieu de les faire exécuter par
l’interpréteur, forcément plus lent que les commandes natives qui
sont compilées.
8.7 Marche aléatoire à 1 dimension.
Un marcheur ivre se déplace sur un trottoir, en faisant un pas aléatoirement vers la droite ou vers la gauche. On simule sa position après pas (on suppose qu’il part de l’origine).
fonction marche(n) local j,x; x:=0; pour j de 1 jusque n faire si alea(2)==1 alors x++; sinon x--; fsi; fpour; return x; ffonction:;
onload
On effectue marches aléatoires et on trace l’histogramme des
positions, par exemple pour et
On peut aussi s’intéresser au nombre de passage par l’origine.
fonction retour(n) local j,x,r; x:=0; r:=0; pour j de 1 jusque n faire si alea(2)==1 alors x++; sinon x--; fsi; si x==0 alors r++; fsi; fpour; return r; ffonction:;
onload
et conjecturer son espérance par observation d’un nombre important
de marches
On peut aussi observer l’espérance en fonction du nombre de pas.
Pour cela, on approche l’espérance par exemple par la moyenne de
1000 nombre de retours.
fonction moyenneretour1(n) local s,j; s:=0; pour j de 1 jusque 1000 faire s := s+retour(n); fpour; return s/1000; ffonction:;
onload
Comme pour les aiguilles de Buffon, le temps de calcul est long (c’est pour cela que l’on a mis un pas de 20 dans la commande précédente), mais il est possible d’accélérer en faisant plusieurs optimisations :
- au lieu de décaler de 1 ou -1, on peut décaler de 1 ou 0 en décidant que l’origine se décale elle-même de 1/2.
- le passage par l’origine ne peut s’effecteur qu’après un nombre pair de pas, on peut donc faire 2 pas à la fois avant de tester. À chaque itération, on fait donc deux pas de valeur 1 ou 0 et l’origine est décalée de 1.
- on remplace les boucles les plus internes par du calcul sur des listes.
fonction moyenneretour(N,n) // N experiences, marche de n local k,pos,r; pos:=seq(0,N); // N marcheurs a l'origine r:=0; n:=iquo(n,2); pour k de 1 jusque n faire pos:=pos+randvector(N,2)+randvector(N,2); r:=r+count_eq(k,pos); // nombre de passage origine translatee fpour; retourne r/N; ffonction:;
onload
On peut maintenant faire le tracé avec une bonne précision en
quelques secondes :
8.8 Les urnes de Polya
On place dans une urne boules rouges et boules bleues, indiscernables au toucher. On tire une boule, on note sa couleur, et on remet dans l’urne cette boule ainsi qu’une boule de même couleur (le nombre total de boules augmente donc de 1). On recommence, on effectue en tout tirages.
On fait une simulation, on va renvoyer la liste des proportions de boules rouges dans l’urne au cours de l’expérience. Il y a boules rouges parmi , on convient donc que la boule tirée est rouge si un tirage d’entier dans est strictement inférieur à .
fonction polya(r,b,n) local L,j; L:=[r/(r+b)]; pour j de 1 jusque n faire si alea(r+b)<r alors r++; sinon b++; fsi; L[j]:=r/(r+b); fpour; return L; ffonction:;
onload
Exemple :
Simulation avec 1000 tirages et 1 boule rouge et 1 boule bleue au début :
Dans la version HTML, si on clique plusieurs fois, on observe que la valeur
limite est très variable.
On peut s’intéresser à la répartition de cette proportion limite
de boules rouges (obtenue ici au bout de 1000 étapes). On modifie
la fonction précédente pour ne renvoyer que la dernière proportion.
fonction polya1(r,b,n) local j; pour j de 1 jusque n faire si alea(r+b)<r alors r++; sinon b++; fsi; fpour; return r/(r+b); ffonction:;
onload
Puis on fait l’histogramme des fréquences pour expériences
Le temps de calcul commence
à s’allonger, on peut essayer d’optimiser un peu. On observe
que le nombre total de boules augmenté de 1 à chaque tirage,
il suffit donc de suivre la valeur de
fonction polya2(r,b,n) local j,total,totalalafin; totalalafin:=r+b+n-1; pour total de r+b jusque totalalafin faire si alea(total)<r alors r++; fsi; fpour; return r/(total-1); ffonction:;
onload
Une autre façon d’optimiser consiste à observer que j
ne sert pas dans la boucle pour
de polya1
.
fonction polya3(r,b,n) seq(ifte(alea(r+b)<r,r++, b++),n); return r/(r+b); ffonction:;
onload
Pour , on s’aperçoit que
la distribution semble uniforme. Ceci n’est plus le cas pour
d’autres valeurs de et , dans la version HTML, vous pouvez
tester diverses valeurs initiales, par exemple 5,5 ou 1,5.
Chapitre 9 Les algorithmes du document ressource Python d’eduscol
On propose dans ce chapitre des versions Xcas de programmes Python donnés dans ce document
Pour les statistiques, voir le chapitre 8.
Pour la dichotomie, voir 4.1.
9.1 Arithmétique
On renvoie au manuel de programmation de Xcas (menu Aide, Manuels), on donne ici rapidement les équivalents Xcas du document eduscol.
9.1.1 Euclide
On calcule le PGCD de et en observant que si est nul c’est ,
sinon c’est le PGCD de et du reste de la division euclidienne de par
(irem(a,b)
)
fonction pgcd(a,b) tantque b!=0 faire a,b:=b,irem(a,b); ftantque; return a; ffonction:;
onload
9.1.2 Écriture en base 2
L’écriture de en base 2 est l’écriture de à laquelle on ajoute le reste de la division par 2.
fonction base2(n) local l; l:=[]; tantque n>0 faire l:=append(l,irem(n,2)); n:=iquo(n,2); ftantque; return revlist(l); ffonction:;
onload
Il faut inverser l’ordre des éléments dans la liste
avant de la renvoyer, parce qu’on calcule
en premier le dernier bit de l’écriture de en base 2.
L’opération inverse se fait en utilisant le même principe : lorsqu’on ajoute un bit à la fin de l’écriture de en base 2, cela revient à multiplier par 2 et lui ajouter la valeur de ce bit.
fonction base10(l) local n,x; n:=0; pour x in l faire n=2*n+x; fpour; return n; ffonction:;
onload
La taille de l’écriture en base 2 s’obtient par dim(l)
ou directement par le programme suivant
fonction taille2(n) local l; l:=0; tantque n>0 faire n:=iquo(n,2); l++; ftantque; return l; ffonction:;
onload
9.1.3 Test naïf de primalité
On teste si est divisible par un entier .
fonction estpremier(n) local d; si n<2 alors return false; fsi; d:=2; tantque d*d<=n faire si irem(n,d)=0 alors return false; fsi; d++; ftantque; return true; ffonction:;
onload
9.1.4 Factorisation naïve
Au lieu de s’arrêter et d’indiquer que le nombre n’est pas premier lorsqu’on a une division exacte par , on ajoute à la liste des facteurs et on divise par . Lorsqu’on sort de la boucle extérieure, vaut 1 ou est premier, il ne faut pas oublier de l’ajouter à la liste des facteurs.
fonction facto(n) local l,d; l:=[]; d:=2; tantque d*d<=n faire tantque irem(n,d)=0 faire l:=append(l,d); n:=n/d; ftantque; d++; ftantque; si n>1 alors l:=append(l,n); fsi; return l; ffonction:;
onload
Exercice :
Comparer l’efficacité de l’algorithme précédent avec l’algorithme du
document eduscol :
fonction facto1(n) local l,d; l:=[]; d:=2; tantque n>1 faire si irem(n,d)=0 alors tantque irem(n,d)=0 faire l:=append(l,d); n:=n/d; ftantque; fsi; d++; ftantque; return l; ffonction:;
onload
On pourra compter le nombre d’itérations si est premier.
9.2 Longueur d’un arc de courbe
On cherche la longueur approchée d’un arc de courbe d’équation entre les points d’abscisses et . Pour cela on approche l’arc de courbe par une ligne polygonale composée de petits segments reliant les points de la courbe d’abscisses et pour et .
fonction longueurcourbe(f,a,b,n) local l,k,h; h:=evalf((b-a)/n); l:=0; pour k de 0 jusque n-1 faire l:=l+distance(point(a+k*h,f(a+k*h)),point(a+(k+1)*h,f(a+(k+1)*h))); fpour; return l; ffonction:;
onload
Exercice :
Cette fonction longueurcourbe est relativement inefficace, car on calcule
plusieurs fois les mêmes quantités. Utilisez des variables locales
pour la rendre plus efficace.
Solution :
fonction longueurcourbe(f,a,b,n) local l,k,h,x,M,N; h:=evalf((b-a)/n); l:=0; x:=a; M:=point(x,f(x)); pour k de 0 jusque n-1 faire x:=x+h; N:=point(x,f(x)); l:=l+distance(M,N); M:=N; fpour; return l; ffonction:;
Remarque : est approché, donc en effectuant la boucle on
va accumuler des erreurs d’arrondis. Si l’erreur est faite par
excès, la dernière valeur de risque d’être un peu
supérieure à 1, et devient alors complexe, avec
une partie imaginaire très petite. Ceci n’a pas d’importance
dans Xcas, car l’instruction point(x,y)
renvoie
le point d’affixe . Dans d’autres langages, par exemple Python,
cela peut provoquer une erreur.
Exercice :
Faire afficher la ligne polygonale dont on calcule la longueur.
Solution :
fonction approxcourbe(f,a,b,n) local L,k,h,x,M,N; h:=evalf((b-a)/n); x:=a; M:=point(x,f(x)); L:=[M]; pour k de 0 jusque n-1 faire x:=x+h; N:=point(x,f(x)); L:=append(L,N); M:=N; fpour; return polygone_ouvert(L); ffonction:;
9.3 Réfraction et recherche de minimas
On se donne une courbe d’équation séparant deux milieux d’indices et et on cherche le chemin lumineux reliant deux points et situés de part et d’autre de la courbe, il s’agit de deux segments de droite et où est le point de la courbe tel que la somme est minimal. Comme a pour coordonnées , on peut calculer cette somme en fonction de Il s’agit donc de déterminer le minimum de la fonction (en supposant connue la fonction ).
9.3.1 Minimum d’une fonction
On commence par la recherche approchée du minimum d’une fonction . On se donne un point de départ et un pas , on regarde si on diminue la valeur de en se déplaçant vers la gauche. Si c’est le cas, on continue à se déplacer vers la gauche jusqu’à ce que ne diminue plus, on a alors atteint un minimum local relativement à la discrétisation choisie. Si ce n’est pas le cas, on essaie de se déplacer vers la droite.1
fonction minih(g,x,h) // recherche d'un minimum local de g a partir de x en se deplacant // par pas de h vers la gauche ou vers la droite si g(x-h)<g(x) alors tantque g(x-h)<g(x) faire x:=x-h; ftantque; sinon tantque g(x+h)<g(x) faire x:=x+h; ftantque; fsi; retourne x; ffonction:;
onload
Exemple :
Recherche du minimum de la fonction en partant
de 2.0 (prendre le carré d’une expression, ici , et en chercher
le minimum est une méthode de recherche d’une solution approchée)
Attention ! ce programme peut tourner en boucle indéfiniment si
décroit indéfiniment. Un programme plus robuste fixera un nombre
maximal d’itérations, par exemple ici 1000 par défaut.
fonction minih(g,x,h,N=1000) local j; si g(x-h)<g(x) alors pour j de 1 jusque N faire si g(x-h)>g(x) alors return x; fsi; x:=x-h; fpour; sinon pour j de 1 jusque N faire si g(x+h)>g(x) alors return x; fsi; x:=x+h; fpour; fsi; retourne "non trouve"; ffonction:;
Exercice :
Améliorer ce programme pour calculer une seule
fois par itération.
Solution :
Pour avoir une valeur de avec une précision fixée à l’avance,
on commence avec un pas de (par défaut 0.1) et on divise le pas
par 10.
fonction mini(g,x,h=0.1,eps=1e-10) // recherche d'un minimum local avec une precision de eps tantque h>eps faire x:=minih(g,x,h); h:=h/10; ftantque; retourne minih(g,x,h); ffonction:;
onload
On effectue un dernier appel à minih
pour assurer que la
précision est meilleure que 1e-10
.
Exercice :
expliquer pourquoi il est plus rapide d’appeler
mini(g,1.0,0.1,1e-10)
que minih(g,1.0,1e-10)
.
9.3.2 Application à la réfraction
Appliquons maintenant au problème de la réfraction.
La fonction refraction
prend en arguments la fonction définissant la courbe séparatrice,
les points et et les indices de réfraction, elle
renvoie le point .
fonction refraction(f,A,B,n1,n2) local g,x,tst; si f(abscisse(A))<ordonnee(A) ou f(abscisse(B))>ordonnee(B) alors return "A et B sont du meme cote du graphe!"; fsi; g(x):=n1*distance(A,point(x,f(x)))+n2*distance(B,point(x,f(x))); x:=mini(g,abscisse(milieu(A,B))); return point(x,f(x)); ffonction:;
onload
Illustration avec une fonction linéaire ,
la séparatrice est la droite d’équation
Pour tester avec une courbe non linéaire, par
exemple avec la fonction , il suffit de modifier la définition
de :
9.3.3 Discussion
L’application à la réfraction
est certainement trop difficile pour des élèves débutants, on
définit en effet ici une fonctionnelle (fonction dont un argument
est une fonction), et on définit une fonction dans une
autre fonction (la fonction ). De plus la définition
de ci-dessus n’est pas recommandée, car elle
dépend de la valeur de paramètres et qui
ne sont pas passés en argument
( n’est pas une fonction
pure, i.e. indépendante du contexte).
Pour rendre indépendante du contexte, il faut remplacer
dans son expression
n1*distance(A,M)+n2*distance(B,M)
par leurs valeurs. Dans Xcas, on peut le faire
en utilisant le moteur de calcul formel. On utilise une variable
symbolique x
pour calculer l’expression de ,
c’est le rôle de la commande purge(x)
ci-dessous,
et on utilise la commande unapply
qui permet de définir
une fonction à partir d’une expression.
fonction refraction(f,A,B,n1,n2) local g,M,x,tst,xM; tst:=(f(abscisse(A))-ordonnee(A))*(f(abscisse(B))-ordonnee(B)); si tst>=0 alors return "A et B sont du meme cote du graphe!"; fsi; purge(x); M:=point(x,f(x)); g:=unapply(n1*distance(A,M)+n2*distance(B,M),x); xM:=mini(g,abscisse(milieu(A,B))); return point(xM,f(xM)); ffonction:;
On a aussi amélioré le test que les deux points et
sont bien de part
et d’autre de la courbe
en introduisant une variable locale tst
qui doit être négative.
Dans des langages non formels, pour faire les choses proprement, il
faut passer explicitement les paramètres A,B,n1,n2
à la
fonction g
, en les regroupant (dans une liste dans un langage
de script, par un pointeur vers une structure en C).
Le prototype de la fonction g
est alors g(x,params)
,
celui des fonctions mini
est mini(g,param,x,h,eps)
. Mais
ceci complique bien sur la présentation au niveau du lycée.
9.3.4 Autre méthode de recherche de minimum
On suppose ici qu’on cherche le minimum d’une fonction sur un intervalle avec d’abord décroissante puis croissante. On suppose donc qu’on a un triplet tel que est plus petit que et On effectue ensuite une sorte de dichotomie. On se fixe une précision
- On effectue une boucle tant que
- On calcule la valeur de au milieu de . Si est plus petit que et on peut utiliser le nouveau triplet , on passe à l’itération suivante.
- Sinon, on calcule la valeur de au milieu de . Si est plus petit que et on peut utiliser le nouveau triplet , on passe à l’itération suivante.
- Sinon, est plus grand que ou que donc est plus grand que puisque , et de même est plus grand que , donc on utilise le nouveau triplet et on passe à l’itération suivante.
Le programme se termine (En effet, si on applique la troisième règle, est divisé par 2. Si on applique uniquement la 1ère et la 2ème règle, si on applique deux fois de suite la première règle est remplacé par puis par le précédent donc on gagne un facteur 2, de même pour la deuxième règle, si on applique la première puis la deuxième est remplacé par et par donc on gagne aussi un facteur 2).
fonction mini2(g,a,b,c,eps=1e-10) local u,v; si c<=a ou c>=b alors return "erreur"; fsi; si g(c)>=g(a) ou g(c)>=g(b) alors return "erreur"; fsi; tantque b-a>eps faire u:=(a+c)/2; si g(u)<g(a) et g(u)<g(c) alors a,c,b:=a,u,c; continue; fsi; v:=(b+c)/2; si g(v)<g(c) et g(v)<g(b) alors a,c,b:=c,v,b; continue; fsi; a,c,b:=u,c,v; ftantque; return a,b,c; ffonction:;
onload
Exercice :
Rendre ce programme plus efficace en calculant le moins de fois
possible les images par de .
9.4 Solveur de triangle
On cherche à construire un triangle dont les cotés sont donnés par où vérifient les inégalités triangulaires On peut par translation se ramener au cas où est l’origine, puis par rotation à , il s’agit donc de déterminer les coordonnées de .
c:=3; x:=1; y:=2; A:=point(0,0,legende="A(0,0)"); B:=point(c,0,legende="B(c,0)"); C:=point(x,y,legende="C(x,y)"); triangle(A,B,C);
onload
On a deux équations
En en faisant la différence, on obtient
d’où l’on tire la valeur de
puis la deuxième équation nous donne deux valeurs pour (correspondant
à deux triangles symétriques)
Remarque :
Puisque vérifient les inégalités triangulaires
si alors
, en effet :
donc
.
Donc a le même signe que .
Ce signe est positif puisque d’après les inégalités triangulaires on a :
donc et
et donc donc .
fonction trisolve(a,b,c) local x,y; x:=(c^2+b^2-a^2)/(2*c); y:=sqrt(b^2-x^2); return point(0,0),point(c,0),point(x,y); ffonction:;
onload
Remarque :
Le document ressource Python eduscol utilise une méthode
de résolution approchée pour déterminer le triangle. Cela nous
semble peu adapté, car la résolution exacte est accessible
au niveau lycée et est beaucoup plus simple.
- 1
- Cette méthode se généralise en dimension plus grande, on cherche la direction de plus grande pente et on se déplace dans cette direction d’un pas constant
Chapitre 10 Aide
10.1 Les fonctions usuelles avec Xcas
abs : valeur absolue ou le module de l’argument.
ceil : renvoie le plus petit entier >= à l’argument.
cos : renvoie le cosinus de l’argument.
Digits : pseudo-variable pour modifier le nombre n de chiffres
significatifs (par ex Digits:=n).
evalf : évaluation numérique du premier argument (le nombre de digits
peut être donné comme second argument).
floor : renvoie la partie entière de l’argument (le plus grand entier
<= à l’argument.
frac : partie fractionnaire de l’argument.
max : renvoie le maximum des éléments d’une séquence ou d’une liste de réels.
min : renvoie le minimum des éléments d’une séquence ou d’une liste de réels.
+ : renvoie la concaténation de 2 chaînes ou addition terme à terme
de 2 expressions ou 2 listes (opérateur infixé).
round : renvoie l’argument réel arrondi à l’entier (ou le décimal)
le plus proche.
sin : renvoie le sinus de l’argument.
sign : renvoie le signe (-1,0,+1) de l’argument.
sqrt : renvoie la racine carrée de l’argument.
tan : renvoie la tangente de l’argument.
10.2 Les fonctions Xcas de calcul formel utilisées
coeff(P,var,[n] : renvoie la liste des coefficients d’un polynôme
P par rapport à la variable Var ou le coefficient de
degré n.
count(f,L) : applique la fonction f aux éléments de la liste
L et en renvoie la somme.
degree(P,var : renvoie le degré du polynôme P par rapport à la
variable var.
dim : renvoie la longueur d’une liste, d’une séquence ou d’une chaîne de caractères.
droit : renvoie le côté droit d’une équation, d’un intervalle,
d’une liste ou d’une chaîne .
fsolve ou resoudre_numerique : renvoie la solution numérique d’une équation.
gauche : renvoie le côté gauche d’une équation, d’un intervalle,
d’une liste ou d’une chaîne.
iquo : renvoie le quotient euclidien de 2 entiers.
iquorem : renvoie la liste du quotient et du reste euclidien de 2
entiers.
irem : renvoie le reste euclidien de 2 entiers.
normal : renvoie une simplification de l’argument.
purge(a) : enlève la valeur stockée dans la variable a.
simplify ou simplifier : renvoie une simplification de l’argument.
solve ou resoudre : renvoie la solution d’une équation.
sommets : renvoie la liste des sommets d’un polygone.
subst ou substituer : remplace dans une expression, une variable non affectée par
une valeur.
sum ou somme : somme des éléments d’une liste (ou séquence).
subtype : renvoie 1 pour une séquence, 2 pour un ensemble, 10 pour un
polynôme et 0 sinon ce qui définit le sous-type de l’argument.
type : renvoie dans [1..12] définissant le type de l’argument.
10.3 Les fonctions Xcas de géométrie utilisées
affichage : employé comme option de commandes graphiques permet
de faire des dessins en couleur (par ex
A:=point([1,1],affichage=rouge)).
carre : renvoie et dessine le carré direct donné par 2
points.
centre : renvoie et dessine le centre du cercle donné en argument.
cercle : renvoie et dessine le cercle donné par 1 point et 1 réel (son
centre et son rayon) ou par 2 points (son diamètre) ou par son équation.
demi_droite : renvoie et dessine la demi-droite donnée par son origine
et 1 point ou par son origine et son vecteur directeur [1,m].
droite : renvoie et dessine la droite donnée par 2 points ou par 1 point
et son vecteur directeur [1,m].
est_aligne : renvoie 1 si les points sont alignés, 2 si les points sont
confondus et 0 sinon.
est_parallele : renvoie 1 si 2 droites sont parallèles et 0 sinon.
equation : renvoie l’équation de l’argument.
inter dessine et renvoie la liste des points d’intersection de 2 objets géométriques.
inter_unique renvoie et dessine un des points d’intersection de 2 objets géométriques.
longueur : renvoie la longueur du segment défini par 2 points
ou par les coordonnées de ces points.
norm : renvoie la norme l2 d’un vecteur :
norm : renvoie
point : renvoie et dessine le point de coordonnées l’argument.
polygone : renvoie et dessine le polygone donné par une liste de
points.
polygone_ouvert : renvoie et dessine la ligne polygonale donnée par une
liste de points.
rayon :renvoie le rayon du cercle donné en argument.
segment : renvoie et dessine soit le segment donné par 2 points ou
le vecteur donné par un point et son vecteur directeur. Bien voir la
différence entre segment(point(cA),point(cB)) (c’est le segment (A,B))
et segment(cA,cB) qui est aussi segment(point(cA),point(cA+cB)).
sommets : renvoie la liste des sommets du polygone donné en argument et les dessine.
translation(B-A,C) : renvoie et dessine le translaté de C dans la
translation de vecteur AB.
triangle : renvoie et dessine le triangle donné par 3 points.
vecteur : renvoie et dessine le vecteur donné par 2 points ou par un
point et son vecteur directeur. Bien voir la
différence entre vecteur(point(cA),point(cB))(c’est le vecteur (A,B)) et
vecteur(cA,cB) qui est aussi vecteur(point(cA),point(cA+cB)).
10.4 Les fonctions Xcas de programmation utilisées
break : pour interrompre une boucle (tantque <cond> faire inst1;
si <cond> alors inst2;break;fsi;ftantque;).
fpour
fsi
ftantque
jusque
local
pas
pour <k> de <k1> jusque <k2> faire <instructions> pas <p> fpour
quand
retourne
si <condition> alors <instructions1> sinon <instructions2> fsi
tantque <condition> faire <instructions> ftantque
10.5 Les fonctions Xcas utilisées en statistiques
alea(n1,n2) : renvoie un réel aléatoire de [n1,n2[.
alea(n) : renvoie un entier aléatoire entre 0 et n-1.
binomial : peut être employé comme option de la commande randvector.
binomial_cdf(n,p,x1,x2) : renvoie Proba(x1<=X<=x2) quand X suit la loi
binomiale B(n,p).
mean : renvoie la moyenne d’une liste ou d’une liste pondérée par le
deuxième argument.
median : renvoie la médiane d’une liste ou d’une liste pondérée par
le deuxième argument.
plotlist : lorsque l’argument est , trace la ligne
polygonale reliant les points d’abscisse et d’ordonnée pour
.
randvector : renvoie une liste de nombres aléatoires de taille
constituée d’entiers aléatoires distribués selon la loi indiquée.
stddev : renvoie l’écart-type d’une liste ou d’une liste pondérée
par le deuxième argument.
Annexe A Xcas, Python et Javascript.
A.1 Le mode de compatibilité Python de Xcas
Xcas accepte une syntaxe compatible avec le langage Python. La principale différence entre la programmation en français et Python, c’est qu’il n’y a pas de fin de bloc explicite en Python, c’est l’indentation qui joue ce rôle. L’indentation consiste à ajouter au début de chaque ligne d’un même bloc le même nombre d’espaces. Il est recommandé d’indenter les programmes en mode Xcas, en mode Python c’est obligatoire. Il faut donc faire attention :
- lorsqu’on copie un programme Python avec un copier-coller : cela risque de modifier l’indentation,
- lorsqu’on écrit un texte contenant un programme Python de faire en sorte que ce programme apparaisse sur une seule page (ce qui n’est pas toujours possible pour un programme contenant beaucoup d’instructions !).
Attention, le mode de compatibilité Python de Xcas n’est pas identique à Python. Seules les constructions de base sont acceptées (pas de programmation orientée objet) :
- def pour définir une fonction,
- if pour un test,
- for pour une boucle pour,
- while pour une boucle tantque.
Certaines commandes Xcas sont compatibles avec Python, par exemple range, assert, random, etc. Notez que :
- on doit déclarer explicitement les variables locales (avec local <nom-var>). Ainsi une faute de frappe dans un nom de variable est détectée et un avertissement est affiché.
- les chaînes de caractères sont délimitées par " car
dans Xcas on réserve ’ pour quoter un argument c’est à dire
pour ne pas évaluer un argument, par exemple :
"a" désigne la lettre a (c’est une chaîne de 1 caractère), et si a:=2+2 (ou en Python a=2+2) a renvoie 4 alors que ’a’ désigne la variable a non évaluée et donc ’a’ renvoie le nom a de la variable a (ce n’est pas une chaîne de 1 caractère, c’est un identificateur).
On aura par exemple :
"a"+3 renvoie "a3" mais
si a:=4 alors ’a’+3 renvoie 3+a et a+3 renvoie 7
type("a") renvoie string (chaîne de caractères)
type(a) renvoie integer (a contient un entier)
type(’a’) renvoie identifier (’a’ est un nom de variable). - les commandes graphiques des librairies Python
ne sont pas implentées, il faut utiliser les commandes de Xcas
(par exemple
plot
,plotlist
,histogram
, ...)
Exemple : On veut faire un programme qui teste si un nombre est parfait : un entier naturel est parfait si il est égal à la somme de ses diviseurs (1 compris, non compris).
On écrit la fonction Parfait qui a comme argument un entier n.
On définit les variables locales à la fonction :
- s qui recevra la somme des diviseurs de n. au début s est nul.
- j qui parcourt les diviseurs potentiels,
Pour tous les entiers j entre 1 et n-1 on teste si j divise n et pour cela on cherche le reste de la division de n par j, c’est irem(n,j) ou n%j qui renvoie ce reste. Si le reste est nul, c’est que j divise n donc on ajoute j à s. Quand j a parcouru tous les entiers jusque n-1 (i.e. quand la boucle pour est terminée), on teste si s est égal à n. Si il y a égalité alors n est parfait, sinon n n’est pas parfait.
Une implémentation en syntaxe Xcas en français :
fonction Parfait(n) local j,s; "Parfait(n) renvoie vrai si n est la somme de ses diviseurs"; s:=0; pour j de 1 jusque n-1 faire si irem(n,j)==0 alors s:=s+j; // j divise n, on l'ajoute a la somme fsi; fpour; si s==n alors // on pourrait aussi recrire return s==n retourne vrai; sinon retourne faux; fsi; ffonction:;
L’indentation permet de visualiser facilement la forme du programme, ici on a
3 instructions : 1 affectation, 1 boucle pour qui contient une instruction
(1 test si) et 1 test si/sinon.
L’indentation sert aussi à contrôler qu’on n’a pas fait
de faute de syntaxe (non-fermeture d’une parenthèse par exemple).
En syntaxe compatible Python, le programme Parfait
précédent s’écrit :
def Parfait(n): local j,s '''Parfait(n) determine si l'entier n est la somme de ses diviseurs''' s=0 for j in range(1,n): if n%j==0: s=s+j # j divise n, on l'ajoute if s==n: return True else: return False
En Python natif, il faut enlever la ligne local j,s
.
A.2 Xcas et Javascript
Javascript est le langage des navigateurs, il est donc
extrêmement répandu et peut être utilisé
sans installation sur PC/Mac. De ce fait, un programme
écrit en javascript pourra facilement
être intégré dans une petite interface
utilisateur en HTML (voir l’exemple ci-dessous),
et exécuté depuis n’importe
quel autre navigateur, ce qui peut être assez
motivant.
Du point de vue des types
de variables utilisables, on dispose entre autres
de nombres approchés,
de chaines de caractères et de listes, il n’y a pas de type
particulier pour les entiers. Les structures de controle ont une
syntaxe proche du langage C, point-virgule en fin d’instruction,
délimiteurs de blocs explicites {}
, test en
if () { ... } else { ... }
et boucle en
for(init;condition;incrementation){...}
ou while(condition){...}
, déclaration de variable
par var
et retour de fonction par return
.
Si vous connaissez javascript, vous pouvez utiliser une syntaxe très
proche dans Xcas. Exemple, syntaxe utilisable dans les
deux langages pour le calcul du -ième nombre de Fibonacci :
function fibo(n){ var u,v,w,j; u=1; v=1; if (n<2) return 1; for (j=2;j<=n;j++){ w=u+v; u=v; v=w; // ou en Xcas (u,v)=(v,u+v); } return v; }
Pour avoir une interface HTML minimaliste pour cette fonction,
on crée un fichier nommé disons fibo.html
(que l’on
consultera depuis un navigateur) contenant :
<script> function fibo(n){ var u,v,w,j; u=1; v=1; if (n<2) return 1; for (j=2;j<=n;j++){ w=u+v; u=v; v=w; } return v; } </script> Le <textarea cols="5" rows="1" id="input" onkeypress="if (event.keyCode!=13) return true; document.getElementById('output').value=fibo(eval(value)); return false;">5</textarea> ième nombre de Fibonacci est <textarea rows="1" id="output"></textarea>
On peut utiliser les commandes de Xcas depuis Javascript, c’est exactement ce que fait l’interface Xcas pour Firefox.
A.3 Xcas et Python
Python est un langage assez populaire dans les milieux scientifiques et est facile à installer. Il possède un type spécifique pour les entiers. Sa principale différence par rapport aux autres langages est que les structures de controle n’ont pas de délimiteur explicite de fin de bloc, c’est l’indentation qui joue ce role. L’obligation d’indenter correctement a certes des vertus pédagogiques, mais pas l’absence de délimiteur explicite de fin de bloc. Ceci rend aussi l’échange de code source Python avec des applications orientées texte (copier/coller, mail, forums, traitement de texte) plus fragile1, ou même la lecture difficile si le code est affiché sur plus qu’une page. De plus, la déclaration de variables locales dans une fonction est implicite, ce qui n’est pas très pédagogique. D’autre part, la syntaxe de la boucle for en Python utilise des objets plus abstraits (itérateurs) ou qui peuvent être inutiles (liste d’entiers en Python 2), elle s’éloigne de celle de la grande majorité des autres langages, y compris la traduction en langage machine. Ainsi l’exemple de Fibonacci donne
def fibo(n): u=1 v=1 if n<2: return 1 for j in range(2,n+1): w=u+v u=v v=w return v
A.4 Comparaison rapide
La principale différence entre les 3 langages, c’est que Xcas est un logiciel de calcul formel et permet de travailler avec des variables symboliques et des expressions. Dans Xcas, une variable globale peut ne pas avoir de valeur (son évaluation la renvoie), ce n’est pas le cas en Javascript ou en Python (l’évaluation d’une variable non initialisée renvoie une erreur). De ce fait, il ne faut pas oublier de déclarer les variables locales d’une fonction pour éviter des erreurs. En contrepartie pour réaliser des fonctions comme la dichotomie ou la recherche d’une valeur approchée d’intégrale par la méthode des rectangles, on peut passer une expression symbolique en paramètre et l’évaluer en un point, ce qui est conceptuellement plus simple que de passer une fonction en argument à une autre fonction. Avec Xcas, on peut aussi faire du calcul symbolique à l’intérieur d’une fonction, par exemple dériver une expression, calculer des valeurs exactes ou approchée, et dans ce dernier cas travailler avec un nombre arbitraire de décimales.
Des trois langages, Javascript est le plus rapide, puis Python puis Xcas.
- 1
- Ainsi, l’exercice de spécialité du sujet de bac S 2017 centres etrangers demande aux candidats d’interpréter un algorithme écrit à la Python sans délimiteurs de blocs explicites ... et l’un des blocs est mal indenté, très probablement au cours d’un passage par un logiciel de traitement de texte
Annexe B Les biais des langages interprétés
Les ordinateurs n’exécutent pas directement les instructions données dans un langage comme Xcas, Javascript, Python, C, Pascal, etc., ils exécutent uniquement du code machine spécifique au micro-processeur de l’ordinateur. Il y a deux façons de traduire le langage en code machine : soit une fois pour toutes lors d’une étape appelée compilation, soit au moment de l’exécution de l’instruction du langage par l’interpréteur (qui est lui-même un programme écrit dans un langage compilé), on parle alors de langage interprété (en toute rigueur, il existe une troisième possibilité un peu intermédiaire qui consiste à compiler au moment où on exécute l’instruction du langage).
Les langages interprétés sont par nature plus faciles à
mettre en oeuvre (pas de compilation) et de ce fait
rendent l’apprentissage plus simple. Mais la traduction au moment
de l’exécution a bien entendu un impact sur le temps d’exécution,
parce que les boucles sont traduites autant de fois
qu’elles sont exécutées (des optimisations peuvent
y remédier plus ou moins, il est d’ailleurs fort possible
que la syntaxe de la boucle for
en Python soit liée
à des considérations d’optimisation).
De plus, les langages interprétés
utilisent des variables dont le type est déterminé
au moment de l’exécution du code, ce qui a un impact aussi
bien pour la taille occupée par la variable que pour l’exécution
d’une opération qui doit commencer par déterminer le type des
arguments pour agir en conséquence. Enfin,
lorsqu’on utilise un langage compilé, on a accès
aux types de données directement manipulables par le
microprocesseur, dont la place occupée en mémoire est optimale
et le temps d’exécution très rapide,
par exemple on peut multiplier deux entiers dont le produit est
inférieur à en moins d’un milliardième de seconde.
Bien entendu, on peut améliorer le temps d’exécution
avec un langage interprété si certaines taches répétitives
sont codées dans une instruction du langage, par exemple
l’instruction effectuant le produit de deux matrices n’est pas
programmé dans le langage interprété lui-même mais est compilé
une fois pour toutes dans le code exécutable de l’interpréteur.
Le programmeur qui souhaite avoir un code suffisamment rapide va
donc adopter un style de programmation qui favorisera l’utilisation
de commandes du langage effectuant en une seule instruction ce
qui nécessiterait une ou plusieurs boucles imbriquées
si on le programmait uniquement avec les
instructions de base d’un langage compilé.
C’est pour cette raison
que les programmeurs expérimentés travaillant avec un
langage interprété écrivent souvent dans un style
“algébrique” avec le moins possible de structures de controle explicites
et des types de données souvent assez complexes.
Par exemple pour écrire un produit scalaire de deux listes en Xcas,
on utilisera la commande dot
, si elle n’existait pas, on
pourrait écrire sum(x[j]*y[j],j,0,size(x)-1)
pour éviter
de faire une boucle pour
, le chapitre statistiques 8
contient quelques exemples d’optimisation de ce type.
Pour des commandes plus compliquées,
on utilisera sans doute des commandes seq
imbriquées au lieu
de boucles.
Mais ce style présente un risque, celui de calculer
plusieurs fois la même quantité : une expression algébrique
n’a pas de variables locales pour stocker de résultats
intermédiaires.
Il faut donc prendre garde au biais de ne
pas calculer des quantités intermédiaires.
Ce n’est pas le seul biais,
il faut également faire attention à l’utilisation
d’instructions faisant des boucles implicitement et
à l’utilisation de types de données puissants qui risquent de masquer
la complexité des opérations nécessaires et pourraient être
remplacés par des types plus simples, par exemple
utiliser un dictionnaire (ou annuaire)
alors qu’un tableau ferait l’affaire.
D’ailleurs d’un point de vue
pédagogique il est toujours bon de savoir ce qu’il y a derrière une boite
noire lorsque c’est accessible au niveau de l’élève ou de l’étudiant.
De plus le style algébrique
est certes compact, mais il est souvent plus difficile à maintenir
(aussi bien à relire qu’à corriger),
et il peut aussi générer une occupation mémoire inutile : par
exemple en simulation, générer une grosse matrice de nombre aléatoires
alors qu’une boucle agissant sur des lignes de cette matrice générées
une par une et effacées après usage est incomparablement
moins gourmand en mémoire.
Enfin, l’optimisation d’un code destiné à être vraiment utilisé
nécessite souvent la compilation des parties critiques, et l’optimisation
de ces parties critiques se fait souvent de manière différente et
parfois opposée aux habitudes acquises en optimisant du code interprété.
Illustrons cela plus en détails
sur un exemple très utile en mathématique: le produit
scalaire de deux vecteurs et le produit d’une matrice
par un vecteur. Il s’agit d’une boucle pour
que l’on code par
fonction ps(u,v) local r,j,n r:=0; pour j de 0 jusque n-1 faire r:=r+u[j]*v[j]; fpour; return r; ffonction
On peut optimiser en utilisant l’instruction d’incrémentation
r += u[j]*v[j];
au lieu de r:=r+u[j]*v[j];
.
Traduit dans un langage compilé, ce code n’est pas loin d’être optimal, par exemple en C++ :
double ps(const vector<double> & u,const vector<double> & v){ size_t j,n=u.size(); double r=0; for (j=0;j<n;j++){ r += u[j]*v[j]; } return r; }
Les variables r,j,n
sont stockées dans des régistres
du microprocesseur. Par itération,
il y a 2 additions d’entiers 64 bits
(adresse de base des vecteurs et valeur de l’indice), 2 lectures
en mémoire de 8 octets (u[j]
et v[j]
),
puis une multiplication de flottants, une addition de flottant,
une incrémentation d’entier 64 bits (l’indice), une comparaison
entre 2 entiers 64 bits, un branchement. Ce qui prend le plus de
temps c’est la lecture en mémoire des double des
vecteurs u
et v
et le branchement/test de fin de boucle.
En langage interprété, ce code sera beaucoup plus lent, car à chaque
itération, pour déterminer la valeur de u[j]
et v[j]
,
il faut évaluer la variable d’indice j
(lue dans l’annuaire
des variables affectées), de même pour les variables u
et v
, s’apercevoir que ce sont bien des tableaux, comparer j
à la taille de u
et de v
(erreur si l’indice est trop
grand), extraire la -ième case
du tableau. Ensuite on fait le produit, ce qui nécessite de tester
le type des variables, on ajoute le résultat à r
ce qui
nécessite à nouveau de tester le type des arguments de +=
.
Puis on incrémente j
de 1 et on compare à n
(qu’il
faut évaluer).
Pour diminuer les opérations de gestion de la boucle,
certains langages interprétés
ont une instruction de boucle spécifique pour itérer sur
les éléments d’une liste, par exemple si on veut afficher les
éléments d’une liste au lieu de faire
fonction aff(u) local j,n pour j de 0 jusque n-1 faire afficher(u[j]); fpour; ffonction;
on écrira
fonction aff(u) local j; pour j in u faire afficher(j); fpour; ffonction;
Cette boucle est implémentée en interne par
une variable de longueur de la liste n
, une autre
d’indice, disons k
,
et j
est évalué par calcul de u[k]
, mais
les opérations de gestion de la boucle
sont pré-compilées et donc plus rapides.
Dans ce type de boucle, le code est plus compact et ne pose aucun
problème de maintenance.
Par contre, pour le produit scalaire,
faire la même chose nécessite d’introduire des objets plus
complexes : on peut imaginer générer la liste des paires
u[j],v[j]
puis itérer sur cette liste. Mais si on ne veut pas créer inutilement
en mémoire une liste de paires, cette liste doit être générée
de manière virtuelle (par exemple au moyen d’une paire de pointeurs
sur les tableaux u
et v
)
et il faut disposer d’une commande capable
de prendre en argument une liste virtuelle. Par exemple en Python
on pourrait écrire sum(i*j for i,j in zip(u,v))
. Un mécanisme
de création de liste virtuelle peut bien entendu avoir un intérêt
intrinsèque, mais il faut avoir conscience que les objets que
l’on manipule sont plus complexes que les listes (qui sont déjà
des objets non triviaux), et que l’utilisation de cet objet dans l’exemple du
produit scalaire pour optimiser une boucle est un artéfact
de langage interprété, et qu’il sera difficile d’optimiser plus avec
ce style de programmation. Il sera d’ailleurs plus facile d’optimiser
en langage compilé sans utiliser ce type d’objets.
Ainsi, il est possible d’optimiser la boucle du produit scalaire, on peut pour grand regrouper plusieurs itérations, et gagner du temps sur les parties test/branchement, par exemple 2 par 2
double ps(const vector<double> & u,const vector<double> & v){ size_t j,n=u.size(); double r=0; n--; for (j=0;j<n;j+=2){ r += u[j]*v[j]+u[j+1]*v[j+1]; } n++; for (;j<n;j++){ r += u[j]*v[j]; } return r; }
Et si on effectue le produit d’une matrice par un vecteur , on peut optimiser le temps d’exécution en mutualisant la lecture des coefficients de , on fait plusieurs produits scalaires avec simultanément.
double ps(const vector<double> & u1, const vector<double> & u2,const vector<double> & v){ size_t j,n=u1.size(); double r1=0,r2=0; n--; for (j=0;j<n;j+=2){ double vj=v[j],vj1=v[j+1]; r1 += u1[j]*vj+u1[j+1]*vj1; r2 += u2[j]*vj+u2[j+1]*vj1; } n++; for (;j<n;j++){ r1 += u1[j]*v[j]; r2 += u1[j]*v[j]; } return r; }
Ici en faisant 2 produits scalaires simultanément, on fait 6 lectures en mémoire au lieu de 8 pour 2 itérations.
Il nous parait donc essentiel pour un scientifique d’apprendre aussi un langage pas trop éloigné de la machine, ou au moins d’être capable de coder avec des objets de base.