Algorithmique et programmation en secondeRenée De Graeve Bernard Parisse |
Table des matières
- Chapitre 1 Avant-propos
- Chapitre 2 Types, fonctions, programmation.
- 2.1 Types
- 2.2 Les fonctions
- 2.3 Les instructions de programmation utilisées sur des exemples
- 2.3.1 Stocker une valeur dans une variable avec :=
- 2.3.2 Enlever une valeur stockée dans une variable avec purge
- 2.3.3 Suite d’instructions
- 2.3.4 L’instruction retourne
- 2.3.5 L’instruction local
- 2.3.6 L’instruction pour
- 2.3.7 L’instruction pour avec un pas
- 2.3.8 L’instruction si
- 2.3.9 Utiliser une fonction utilisateur dans un programme
- 2.3.10 L’instruction tantque
- 2.3.11 Interruption d’une boucle
- 2.3.12 Exemple 9 : autre exemple de boucle tantque
- 2.3.13 Exemple 10 : encore un autre exemple de boucle tantque
- 2.4 Exercices
- Chapitre 3 Résolution d’équations
- Chapitre 4 Les figures en géométrie plane avec Xcas
- Chapitre 5 La géométrie analytique
- 5.1 Les segments
- 5.2 Les droites
- 5.3 Triangles et quadrilatères définis par les coordonnées des sommets
- 5.4 Les vecteurs
- 5.5 Changement de repères
- 5.6 Cercles, Tangentes à un cercle
- 5.6.1 Équation d’un cercle défini par son centre et son rayon
- 5.6.2 Équation d’un cercle défini par son diamètre
- 5.6.3 Équation d’un cercle défini par son centre et son rayon ou par son diamètre
- 5.6.4 Centre et rayon d’un cercle donné par son équation
- 5.6.5 Construire la tangente à un cercle en l’un de ses points
- 5.6.6 Construire la (les) tangente(s) à un cercle passant par un point
- 5.6.7 Solution analytique des tangentes à un cercle
- Chapitre 6 Quelques tests géométriques
- Chapitre 7 Statistiques
- Chapitre 8 Aide
- Annexe A Les biais des langages interprétés
Index
|
|
Chapitre 1 Avant-propos
Ce document est principalement destiné aux enseignants qui souhaitent utiliser le langage de programmation de 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.
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).
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ésisterons aux pressions de certains de vouloir imposer un langage unique (comme pour les enseignements obligatoires d’informatique en classe préparatoires). 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 ou Javascript, Julia, Python, ...), 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).
Le langage de 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). De plus nous avons adapté le langage 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)
De plus l’utilisation de Xcas peut se faire naturellement pendant une séance de cours, pas besoin d’aller en salle informatique, on peut en effet utiliser les smartphones ou 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.
Chapitre 2 Types, fonctions, programmation.
2.1 Types
2.1.1 Les entiers, les rationnels et les nombres approchés.
Dans Xcas :
les entiers sont des nombres de ℤ, par exemple -2,
les rationnels sont des nombres de ℚ, par exemple 1/3,
les nombres approchés sont des nombres décimaux1, par exemple 3.14,
les nombres réels sont représentés par des des nombres décimaux ou par des valeurs symboliques, par exemple √2.
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 √2 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 ,
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 ()) et les éléments
de la séquence sont séparés par une virgule ,
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 ""
Les instructions sur les listes les séquences et les chaînes de caractères
dim(L) renvoie le nombre d’éléments de la liste L.
dim(S) renvoie le nombre d’éléments de la séquence S.
dim(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]
désigne le dernier élément de la liste.
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] 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.
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"
On tape :
2.1.3 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é avec ==
Exemple :
Attention
le signe := sert à stocker une valeur dans une variable et
le signe = sert à définir une équation et non à
tester l’égalité2
Opérateur booléen infixé qui teste la non égalité avec !=
Opérateur booléen infixé qui teste l’inégalité avec <, >, <=, >=
2.1.4 Expressions, polynômes
Simplification d’une expression avec normal
Xcas renvoie le résultat d’un calcul sans le simplifier.
Il faut utiliser la fonction normal ou simplify
pour avoir le résulat simplifié.
Les polynômes
Un polynôme à une indéterminée à coefficients dans ℝ est
déterminé par une
séquence an,...,a1,a0 d’éléments de ℝ, c’est l’expression :
anxn+...+a1x+a0 (resp a0+a1x+a2x2+..+anxn).
n est le degré du polynôme.
On dit que l’on a écrit le polynôme selon les puissances décroissantes
(resp. croissantes).
an,..a1,a0 sont les coefficients du polynôme et x est la variable ou
l’indéterminée du polynôme.
On notera l’ensemble des polynômes à une indéterminée x : ℝ[x].
Un polynôme à 2 indéterminées x et y à coefficients dans ℝ est
déterminé par
une séquence An(y),...,A1(y),A0(y) d’éléments de ℝ[y]
et a pour expression :
A0(y)+A1(y)x+A2(y)x2+..+An(y)xn (resp An(y)xn+...+A1(y)x+A0(y)).
par exemple :
si A0(y)=y3−2,A1(y)=−2y,A2(y)=y3+2*y+3
Le polynôme s’écrit :
y3−2−2y*x+(y3+2*y+3)*x2=x2*y3+2*x2*y+3*x2−2*x*y+y3−2
Le degré par rapport à x du polynôme de cet exemple et égal à 2.
Le degré par rapport à y du polynôme de cet exemple et égal à 3.
Coefficients et degré d’un polynôme
Xcas représente les polynômes :
soit comme la séquence des coefficients selon les puissances décroissantes
(poly1[1,2,3] i.e. [a2,a1,a0]:=[1,2,3])
soit sous la forme d’une expression symbolique (x^
2+2x+3 si x
est l’indéterminée).
Les commandes poly2symb et symb2poly permettent de passer d’une représentation à l’autre :
poly2symb([1,2,3],y) renvoie (y+2)*y+3
et symb2poly(y^
2+2y+3) renvoie poly1[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 par
rapport au 2ième argument (x est la variable par défaut).
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).
On a si L:=symb2poly(P(x)), degree(P(x)) est égal à
dim(L)-1.
Pour avoir le coefficient d’un polynôme par rapport à une variable de
degré donné on utilise l’instruction coeff.
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 controle (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 controle.
2.2.1 Quelques fonctions algébriques de Xcas
abs est la fonction valeur absolue
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 Définition d’une fonction algébrique d’une variable
Exemple :
On veut définir la fonction f définie pour x ∈ ℝ, par
f(x)=x2+1.
f est le nom de la fonction et x est le nom de l’argument de f (x est
ici un réel), la valeur de la fonction est x^
2+1;.
Remarque
En mathématique on dit que x est une variable.
On tape simplement :
On pourrait aussi définir f par un programme
avec fonction...ffonction et retourne :
2.2.3 Définition d’une fonction algébrique de 2 variables
Exemple :
On veut définir la fonction g définie pour (a,b) ∈ ℕ2 par g(a,b)=q,r où
q,r désigne le quotient et le reste de la division euclidienne de a par
b.
On tape simplement :
ou bien avec fonction...ffonction et retourne :
g est le nom de la fonction, a,b sont les noms des arguments de g (a et
b sont des entiers) et iquorem renvoie le quotient et le reste de la
division euclidienne de a par b sous la forme d’une liste.
On a aussi les instructions :
iquo(a,b) qui renvoie le quotient de la division euclidienne de a par
b.
irem(a,b) qui renvoie le reste de la division euclidienne de a par b.
et on a donc iquorem(a,b) est identique à [iquo(a,b),irem(a,b)].
2.2.4 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(x)=|x−1|−1 :
si x<1 on a Abs1(x)=−x+1−1=−x et
si x>1 on a Abs1(x)=x−1−1=x−2
On tape simplement :
ou bien avec fonction...ffonction et retourne :
2.2.5 Connaitre les types et les sous-types
Les types
Xcas sait reconnaitre le type d’un objet.
Pour avoir le type de l’objet a ou le contenu d’une variable a, on
utilise type(a).
Par exemple, si a:=<valeur>, Xcas peut renvoyer :
DOM_FLOAT ou 1 ce qui signifie que a contient un nombre
flottant,
DOM_INT ou 2 ce qui signifie que a contient un nombre
entier,
DOM_RAT ou 10 ce qui signifie que a contient un nombre
rationnel,
DOM_FUNC ou 13 ce qui signifie que a est le nom d’une
fonction,
DOM_LIST ou 7 ce qui signifie que a contient une liste,
DOM_STRING ou 12 ce qui signifie que a contient une
chaîne de caractères,
DOM_SYMBOLIQUE ou 8 ce qui signifie que a contient une valeur
exacte,
DOM_IDENT 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.
Par exemple, Xcas peut renvoyer :
DOM_FLOAT ou 1 ce qui signifie que a contient un nombre flottant,
2.3 Les instructions de programmation utilisées sur des exemples
2.3.1 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 :
2.3.2 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 :
2.3.3 Suite d’instructions
Pour effectuer une suite d’instructions, il suffit de les écrire les unes
à la suite des autres, en terminant chaque instruction par ;
Exemple :
2.3.4 L’instruction retourne
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:
2.3.5 L’instruction local
Exemple 2: notion de variables locales:
On veut, dans cet exemple, définir une fonction h de deux variables a,b
(a et b sont des entiers) qui renvoie le numérateur et le dénominateur
de la fraction a/b simplifiée.
Pour cela il faut diviser a et b 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 c 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 a et b).
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.
2.3.6 L’instruction pour
Exemple 3 : notion de boucle pour
On veut, dans cet exemple, définir une fonction s d’une variable n (n
est un entier) qui calcule la somme : 1+3+...+2n−1.
Pour cela, on utilise une variable locale S que l’on initialise à 0 :
S:=0;
puis on va faire n étapes en utilisant cette variable locale S.
S va contenir successivement :
étape 1 S:=S+(2*1−1); donc S contient 1 (0+1)
étape 2 S:=S+(2*2−1); donc S contient 4 (1+3)
...
étape k S:=S+(2*k−1); donc S contient 1+3+..2k−1
...
étape n S:=S+2*n−1; donc S contient 1+3+..2n−1
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 s(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 : s(n)=1+3+...2n−1=(n)2
On sait que pour tout k ∈ ℕ on a :
k2−(k−1)2=((k−1)+1)2−(k−1)2=2k−1 et
(k+1)2−k2=2k+1
Donc :
1=12−02 (k=1),
3=22−12 (k=2),
5=32−22 (k=3),
...
2k−1=k2−(k−1)2
2k+1=(k+1)2−(k)2
...
2n−1=n2−(n−1)2
Donc :
s(n)=1+3+...2n−1=1+(n)2=1+(4−1)+(9−4)...+(n2−(n−1)2)=n2
En classe de terminales, on peut montrer cette formule par récurrence :
s(1)=1 si s(n)=(n)2 alors on a :
s(n+1)=s(n)+2(n+1)−1=(n)2+2n+1=(n+1)2
La formule est donc montrée par récurrence.
2.3.7 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:;
2.3.8 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:;
2.3.9 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 n d’un même
article de prix P).
En utilisant la fonction Prix(n,P) écrite précédemment,
modifier le programme précédent lorsque le magasin pratique
l’achat en gros.
Solution :
fonction Prix(n,P) local S; si n>=3 alors S:=n*P*0.9; sinon S:=n*P; fsi; retourne S; ffonction:; 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:;
2.3.10 L’instruction tantque
Exemple 7 : Notion de boucle tantque
On utilise une boucle tantque lorsque l’on ne connait 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:;
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, 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 commbien 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.
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.
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 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:; 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 S devient
supérieur à a : 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 k 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. 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. P:=S-L[k].
2.3.11 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.
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.
2.3.12 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 r qui varie selon le montant
a 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 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
(contrairement au programme précédent, on suppose ici
que Paul a suffisamment d’argent).
Il veut
faire un programme qui arrête sa liste dès que S>=a en coupant Lc en 2
listes La liste des objets de ce qu’il achète réellement pour un montant
S avant réduction et Lfin liste des objets qu’il
n’achète pas (Lfin est éventuellement une liste vide).
Paul veut que son programme ait paramètres Lc,a,r et qu’il renvoie :
La, Lfin, S, S−r.
Dans ce cas, on ne
sait pas au départ commbien de fois on doit effectuer la boucle.
Mais on sait quand on doit s’arrêter :
on arrête la boucle lorsque le prix S de la liste complète Lc
n’atteint pas le montant a ou dés que le prix S du début de Lc
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 k 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 La des courses qui ont été prises en compte,
la liste Lfin 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 S>a.
fonction Ticketdimanche(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; pour k de 1 jusque n pas 2 faire S:=S+L[k]*L[k+1]; si S>=a alors La:=gauche(L,k); Lfin:=droit(L,n-k); retourne La,Lfin,S,S-r; fsi; fpour; retourne L,[],S,S; ffonction:;
2.3.13 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 r qui varie
selon le montant a des achats.
Modifier les programmes précédents pour tenir compte de des achats en
gros.
On tape :
fonction Prix(n,P) local S; si n >=3 alors S:=n*P*0.9; sinon S:=n*P; fsi; retourne S ffonction:; 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 Prix(n,P) local S; si n >=3 alors S:=n*P*0.9; sinon S:=n*P; fsi; retourne S ffonction:; fonction Ticketdimgros(L,a,r) local S,n,k,Lfin,La; n:=dim(L); S:=0; pour k de 1 jusque n pas 2 faire S:=S+Prix(L[k],L[k+1]); si S>=a alors La:=gauche(L,k); Lfin:=droit(L,n-k); retourne La,Lfin,S,S-r; fsi; fpour; retourne La,[],S,S; ffonction:;
2.4 Exercices
2.4.1 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:;
- 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
- Xcas accepte toutefois = dans certaines situations non ambigües.
Chapitre 3 Résolution d’équations
3.1 Encadrer une racine d’une équation par dichotomie
Algorithme de dichotomie
On suppose que :
la fonction f est continue et croissante (resp décroissante) sur
l’intervalle [a,b] et que f(a)<0 et f(b)>0
(si f(a)>0 et f(b)<0 on se ramène au cas précédent
en échangeant a et b).
On en déduit que f s’annule pour x=x0 avec a<x0<b.
On cherche une valeur approchée de x0.
Pour avoir une meilleur approximation de x0, on cherche le signe de
f(a+b)/2) (c:=(a+b)/2 est le milieu de [a,b])
Si f(c)==0 alors x0=c et on est content !
Si f(c)<0 alors c<x0<b sinon f(c)>0 alors a<x0<c.
On peut donc recommencer le processus jusquà ce que
f(c)==0 ou abs(b-a)<10^
-n (avec par exemple n:=3).
On tape :
fonction Dichotomie(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 garde f(c)<0 ftantque retourne [c]; ffonction:;
Puis, on tape :
On peut rajouter en début de programme
un test sur n pour que le nombre d’itération
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 10−n et de f(c). Ce qui donne le programme suivant :
fonction Dichotomie(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 garde f(c)<0 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 Dichotomie(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:=evalf((a+b)/2); 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:;
Si on connait les logarithmes, on peut calculer le nombre d’itérations N
pour que |b−a|/2N<10−n 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 à)
21025. 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 a et b sont représentés
par le même nombre flottant (et le test |b−a|<10−n sera
donc vrai) soit ils ne différeront que par leur dernier bit de mantisse,
et dans ce cas c=(a+b)/2 sera arrondi vers a ou vers b et la boucle
tanque continuera indéfiniment.
La majoration est le plus souvent très pessimiste, par exemple
si a=1 et b=2 ils sont déjà représentés avec le même
exposant et le nombre d’itérations sera limité par 48.
fonction Dichotomie(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:=evalf((a+b)/2); 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 f une
seule fois par itération, c’est-à-dire qu’on calcule c et f(c)
mais qu’on ne recalcule pas f(a).
Indication :
On pourra introduire 3 variables locales fa, fb, fc contenant les valeurs
de f(a),f(b),f(c). Ajouter un test pour renvoyer [c] si
f(c) est nul.
Correction de l’exercice
fonction Dichotomie(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:=evalf((a+b)/2); 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:;
3.2 Résoudre 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 "infinité 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:;
3.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é
a1 x+b1 y+c1=0, a2x+b2y+c2=0 |
à deux inconnues x,y. 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 (a1,b1)≠ (0,0) et (a2,b2) ≠ (0,0).
Dans ce cas a1 x+b1 y+c1=0 et a2x+b2y+c2=0
sont les équations de 2 droites D1 et D2.
Solution géométrique
-
Si D1 et D2 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 D1 et D2 sont parallèles il n’y a pas de solution, sauf si
D1 et D2 sont confondues, il y a une infinité de solutions. On
va montrer que cela se produit si et seulement si a1 b2−a2b1 ≠ 0.
En effet, D1 et D2 sont parallèles lorsque les coefficients [a1,b1] et [a2,b2] sont proportionnels i.e si il existe k≠0 tel que :
[a1,b1]=k[a2,b2]=[ka2,kb2] ce qui entraine :
Réciproquement, si (b1a2=b2a1) alors D1 et D2 sont parallèles ou confondues.(b1a2−b2a1)=kb2a2−kb2a2=0
En effet :- Si b1=0 (resp a1=0) alors b2=0 (resp a2=0) puisque [a1,b1]≠[0,0] et (b1a2=b2a1), donc D1 et D2 sont parallèles à l’axe des x (resp y).
- Si b1≠0 et a1≠0 alors b2/b1==a2/a1=k ce qui signifie que
D1 et D2 sont parallèles ou confondues
(D1 et D2 sont confondues lorsque les coefficients [a1,b1,c1]
et [a2,b2,c2] sont proportionnels i.e si il existe k≠0 tel que :
[a1,b1,c1]=k[a2,b2,c2]=[ka2,kb2,kc2]).
On commence par écrire un programme dans le cas où les droites D1 et D2 sont concourrantes.
fonction Intersection(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"+ "(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 a1b2−a2b1, en stockant sa valeur dans
une variable locale.
Correction de l’exercice :
fonction Intersection(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 degré 1"; fsi; Eq2:=normal(gauche(Eq2)-droit(Eq2)); si degree(Eq2,Var1)>1 et degree(Eq2,Var2)>1 alors retourne "pas de degré 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:;
Justification de la solution lorsque D1 et D2 sont concourantes
On a vu que c’était le cas si et seulement si
b1a2−b2a1≠0.
-
Si b1≠0 on a y=(−a1x−c1)/b1 donc l’abscisse du point
d’intersection de D1 et D2 vérifie :
b1a2x+b2(−a1x−c1)+b1c2=0
donc (b1a2−b2a1)x+b1c2−b2c1=0 et finalement
On remplace dans l’expression de y en fonction de xx= b1c2−b2c1 b2a1−b1a2 y = −a1 b1c2−b2c1 b2a1−b1a2 −c1 b1 = −a1b1c2+a1b2c1−c1(b2a1−b1a2) b2a1−b1a2 b1 = −a1b1c2+c1b1a2 b1(b2a1−b1a2) = −a1c2+c1a2 b2a1−b1a2 - Si b1=0, on va voir que les mêmes formules s’appliquent.
En effet l’abscisse du point d’intersection de
D1 et D2 vérifie a1x+c1=0 donc
Comme b2≠0, on ax=− c1 a1 = b1c2−b2c1 b2a1−b1a2 y = −a2x−c2 b2 = a2 c1 a1 −c2 b2 = a2 c1−a1 c2 a1b2 = −a1c2+c1a2 b2a1−b1a2
- 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 4 Les figures en géométrie plane avec Xcas
4.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 2d.
Si on a donné un nom au point (par ex 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 2d.
4.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.
4.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 2d.
equation a comme argument une droite.
equation renvoie l’équation de cette droite
4.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.
4.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 2d.
carre a comme argument 2 points.
carre trace le carré direct défini par ces 2 points sur l’écran de
géométrie 2d.
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 2d.
4.6 Le cercle : cercle
Si le cercle est défini par son centre et son rayon :
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 2d.
Si le cercle est défini par son diamètre :
cercle a pour argument 2 points.
cercle trace le cercle de diamètre ces 2 points.
Chapitre 5 La géométrie analytique
5.1 Les segments
5.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 sqrt((xA-xB)^2+(yA-yB)^2); ffonction:;
5.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:;
5.2 Les droites
5.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 (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 tape :
fonction Droite(cA,L) local xA,xB,yA,yB,m; si type(cA)==DOM_SYMBOLIC alors retourne cA;fsi; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; retourne (xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB=0; sinon m:=L; retourne y-m*(x-xA)-yA=0; fsi; ffonction:;
5.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:;
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:;
5.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 :
a1x+b1y+c1=0 et a2x+b2y+c2=0
Ces 2 droites sont parallèles si a1b2=a2b1.
Si a1b2≠ a2b1 d1 et d2 sont sécantes.
Les coordonnées de leur point d’intersection sont
(−c2*b1+b2*c1)/(−b2*a1+a2*b1),(c2*a1−a2*c1)/(−b2*a1+a2*b1)
Interdroite(d1,d2) renvoie [] si d1 et d2 sont
paralléles et sinon renvoie les coordonées de leur point d’intersection.
On rappelle les programmes Droite et Coeffsdroite
précédents pour
avoir les coefficients
des équations Eq1 et Eq2 de d1 et de d2
fonction Droite(cA,L) local xA,xB,yA,yB,m; si type(cA)==DOM_SYMBOLIC alors retourne cA;fsi; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; retourne (xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB=0; sinon m:=L; retourne y-m*(x-xA)-yA=0; fsi; ffonction:; fonction Coeffsdroite(Eq) local a,b,c,d1,d2; 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:;
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:=a2*b1-b2*a1; si d==0 alors retourne [];fsi; retourne [(b2*c1-b1*c2)/d,(c2*a1-a2*c1)/d]; ffonction:;
5.3 Triangles et quadrilatères définis par les coordonnées des sommets
On définit des versions de la commande polygone
fonction Triangle(cA,cB,cC) retourne polygone(cA,cB,cC); ffonction:; fonction Quadrilatere(cA,cB,cC,cD) retourne polygone(cA,cB,cC,cD); ffonction:;
5.4 Les vecteurs
5.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 :
Vecteur(cA,cB):=normal(cB-cA);
5.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 :
SumVect(cV1,cV2):=normal(cV1+cV2);
5.4.3 Coordonnées de D extrémité du vecteur d’origine C équipollent au vecteur AB
On a D:=C+(B−A).
On tape :
Translation(cA,cB,cC):=cC+(cB-cA);
5.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:;
5.5 Changement de repères
5.5.1 Le problème
Soient 2 repères orthonormés O,Ox,Oy et I,IX,IY.
Notations :
-
Soit M un point ou un vecteur.
On note cM les coordonnées [xM,yM] de M dans le repère Oxy.
On note CM les coordonnées [XM,YM] de M dans le repère IXY. - On note u (resp v) le vecteur unitaire porté par Ox (resp Oy)
- On note U (resp V) le vecteur unitaire porté par OX (resp OY)
Avec ces notations, on a :
- le point I a pour coordonnées cI:=[xI,yI] dans le repère O,Ox,Oy donc OI=xIu+yIv
- le vecteur U a pour coordonnées cU:=[xU,yU] dans le repère O,Ox,Oy ( xU2+yU2=1) donc U=xUu+yUv
- le vecteur V a pour coordonnées cV:=[xV,yV] dans le repère O,Ox,Oy ( xV2+yV2=1) donc V=xVu+yVv. L’angle (U,V)=π/2 donc xV:=−yU et yV:=xU
5.5.2 Le programme Changexy2XY(cM,cI,cU)
On connait les coordonnées cM:=[xM,yM] d’un point M dans le
repère (O,Ox,Oy) ainsi que les coordonnées cI:=[xI,yI] et
cU:=[xU,yU] de I et de U dans le repère (O,Ox,Oy).
On cherche les coordonnées CM:=[XM,YM] de
M dans le repère (I,IX,IY).
On a donc :
|
Donc :
xM=xI+XMxU+YMxV, yM=yI+XMyU+YMyV |
on en déduit XM, YM
XM= |
| , YM= |
|
Or xV=−yU et yV=xU donc
xUyV−yUxV=xU2+yU2=1 |
Finalement :
XM= (xM−xI)xU+(yM−yI)yU, YM= (xM−xI)yU−(yM−yI)xU |
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 :
5.5.3 Le programme ChangeXY2xy(CM,cI,cU)
Il s’agit du programme inverse du précédent : on connait les coordonnées CM:=[XM,YM] d’un point M dans le repère (I,IX,IY) ainsi que les coordonnées cI:=[xI,yI] et cU:=[xU,yU] de I et de U dans le repère (O,Ox,Oy). On cherche les coordonnées cM:=[xM,yM] de M dans le repère (O,Ox,Oy).
On a vu précédemment que :
xM=xI+XMxU+YMxV, yM=yI+XMyU+YMyV |
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:;
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 :
5.5.4 Exercices
En se servant des programmes précédents, faire les programmes :
- calculant les coordonnées cC du sommet C d’un triangle équilatéral direct ABC connaissant les coordonnées cA de A et cB de B,
- calculant les coordonnées cC et cD des sommets C et D d’un carré direct ABCD connaissant les coordonnées cA de A et cB de B.
Solution
1/
Soit un triangle équilatéral direct ABC. Dans le repère
orthonormé Oxy, A a pour coordonnées cA:=[xA,yA] et B a pour
coordonnées cB:=[xB,yB].
Cherchons les coordonnées CC=[XC,YC] du point C dans le
repère d’origine A et d’axe des X dirigé selon le vecteur AB.
On pose :
l:= | √ |
|
On a :
cU:=[ |
| , |
| ], CC:=[ |
| , |
| ] |
Et les coordonnées de C dans le repère orthonormé Oxy sont :
ChangeXY2xy(CC,cA,cU)
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:; 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:;
2/
Soit un carré direct ABCD, avec A de coordonnées
cA:=[xA,yA] et B de coordonnées cB:=[xB,yB]
dans le repère
orthonormé Oxy.
Cherchons les coordonnées CC:=[XC,YC]
du point C et CD:=[XD,YD]
des points C et D dans le repère d’origine A et d’axe des X dirigé
selon le vecteur AB. On pose :
l:= | √ |
|
On a :
cU:=[ |
| , |
| ], cC=[l,l], cD=[0,l] |
Donc les coordonnées de C dans le repère orthonormé Oxy sont :
ChangeXY2xy(CC,cA,cU)
les coordonnées de D dans le repère orthonormé Oxy sont :
ChangeXY2xy(CD,cA,cU).
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:;
5.6 Cercles, Tangentes à un cercle
5.6.1 Équation d’un cercle défini par son centre et son rayon
Le cercle C défini par son centre A de coordonnées [xA,yA] et de rayon r a pour équation :
(x−xA)2+(y−yA)2=r2 |
ou encore
x2+y2−2xAx−2yAy+xA2+yA2−r2=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 Cercle1(cA,r) //cercle défini 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:;
5.6.2 Équation d’un cercle défini par son diamètre
Si les points A et B ont pour coordonnées [xA,yA] et
[xB,yB], le cercle C de diamètre AB 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ètre 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:;
5.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)==DOM_LIST alors xB:=L[0]; yB:=L[1]; cB:=[xB,yB] cM:=Milieu(cA,cB); xM:=cM[0]; yM:=cM[1]; r:=Longueur(cA,cB); retourne [cM,r/2,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:;
5.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:;
5.6.5 Construire la tangente à un cercle en l’un de ses points
Soit C un cercle de centre I de coordonnées
cI=[xI,yI] et de rayon r.
Soit A un point de C de coordonnées cA=[xA,yA].
la tangente au cercle C en A est perpendiculaire à IA, donc
a pour pente m=−(xA−xI)/(yA−yI)
L’équation de cette tangente est donc : Droite(cA,m).
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:;
5.6.6 Construire la (les) tangente(s) à un cercle passant par un point
Soit C un cercle de centre I de coordonnées
cI:=[xI,yI] et de rayon r.
Soit A un point du plan de coordonnées cA:=[xA,yA].
Si A est à l’intérieur de C il n’y a pas de tangente à C
passant par A.
Le cas simple
On suppose qu’on a choisi comme repère le repère IXY
d’origine le centre I du cercle C et
tel que A est sur l’axe des X (i.e. de coordonnées [XA,0]) et
à l’extérieur de C.
On peut mener par A , 2 tangentes T1 et T2 à C.
Soient M1 et M2 les 2 points de tangeance de T1 et T2.
Les triangles IAM1 (resp IAM2) sont rectangles en M1
(resp M2) et M1M2 est perpendiculaire à AI.
Soit H l’intersection de M1M2 avec AI.
On a :
r2=IM12=IM22=IA × IH |
Donc H a pour abscisse r2/XA et pour ordonnée 0.
M1 et M2 ont la même abscisse :
XM1=XM2= |
|
M1 et M2) ont pour ordonnées opposées :
YM1= |
|
| =r |
|
| , YM2=−YM1 |
Les tangentes sont donc :
Si A est sur C (i.e. XA==r) alors on peut mener par A , une tangente T1 à C qui est Droite(x=XA,equation).
Le cas général
On se ramène au cas précédent par changement de repère.
Soit Oxy le repère.
On fait un changement de repère en prenant le centre du cercle
I comme origine et IA comme axe des X.
On a : XA=Longueur(cA,cI)
On note XM et YM les coordonnées d’un point M ou d’un
vecteur M dans le nouveau repère IXY et
xM et yM les coordonnées dans le repère Oxy.
Si U est le vecteur unitaire de IX on a :
xU:=(xA−xI)/XA et yU:=(yA−yI)/XA avec XA:=Longueur(cA,cI).
On a vu que (cf 5.5.3) :
xM=xI+XMxU+YMxV, yM=yI+XMyU+YMyV |
xV:=−yU, yV:= xU |
Donc :
xM=xI+XMxU−YMyU, yM=yI+XMyU+YMxU |
En remplaçant xU et yU par leur valeur, on a :
xM=xI+ |
| yM=yI+ |
|
Dans le repère IXY les coordonnées de M1 et M2) sont :
XM1=XM2=r2/xA |
yM1=r |
|
| , yM2=−r |
|
|
Donc :
|
On tape en utilisant Cercle, Droite et Longueur :
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 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 peut aussi plus simplement utiliser le programme de changement de repère écrit précedemment ChangeXY2xy, ainsi que Longueur, Droite et Cercle, que l’on rappelle ici :
fonction Longueur(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne sqrt((xA-xB)^2+(yA-yB)^2); ffonction:; fonction Droite(cA,L) local xA,xB,yA,yB,m; si type(cA)==DOM_SYMBOLIC alors retourne cA;fsi; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; retourne (xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB=0; sinon m:=L; retourne y-m*(x-xA)-yA=0; fsi; ffonction:; fonction Cercle(cA,L) local cB,xA,xB,xM,yA,yB,yM,cM,r; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; cB:=[xB,yB] cM:=Milieu(cA,cB); xM:=cM[0]; yM:=cM[1]; r:=Longueur(cA,cB); retourne [cM,r/2,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:; 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:;
On tape alors :
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:;
5.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 C passant par un point A.
On pourra se servir des programmes écrits précédement :
Longueur,
Droite,
Cercle,
Solution12.
On considère un repère orthonormé dans lequel le centre I du cercle
C de rayon r a pour coordonnées [xI,yI].
C a pour équation (x−xI)2+(y−yI)2=r2 soit
x2+y2−2xIx−2yIy+xI2+yI2−r2=0 |
Soit A un point de coordonnées [xA,yA].
Si A se trouve à l’extérieur du cercle C, on peut mener par
A, deux tangentes. Les points de contact M1 et M2 de ces
tangentes avec C sont aussi les points d’intersection de C et du
cercle de diamètre IA.
Le cercle de diamètre IA a pour centre K et rayon R
où K est le milieu de IA
de coordonnées [xK,yK]=cK=Milieu(cI,cA) et
R=Longueur(cI,cA)/2.
Ce cercle a donc comme équation (x−xK)+(y−yK)2=R2 i.e.
x2+y2−2xKx−2yKy+xK2+yK2−R2=0 |
Il faut donc résoudre le système d’inconnues x,y
|
qui est équivalent à :
|
On a 2(xI−xK)=(xI−xA) et 2(yI−yK)=(yI−yA)
Or A≠ I donc xA≠ xI ≠ xK ou yA ≠ yI ≠ yK.
Si (yI−yK) ≠ 0 (resp (xI−xA) ≠ ) alors on connait y
(resp x) en fonction
de x (resp y) et il faut résoudre une équation
de degré 2 en x (res y).
Si A est sur le cercle C, on peut mener par
A, une tangente
(c’est une droite passant par A et qui est perpendiculaire à IA.
Si A est à l’intérieur du cercle C, il n’y a pas de
tangente passant par A.
On tape :
fonction Longueur(cA,cB) local xA,xB,yA,yB; xA:=cA[0]; yA:=cA[1]; xB:=cB[0]; yB:=cB[1]; retourne sqrt((xA-xB)^2+(yA-yB)^2); ffonction:; fonction Droite(cA,L) local xA,xB,yA,yB,m; si type(cA)==DOM_SYMBOLIC alors retourne cA;fsi; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; retourne (xA-xB)*y-(yA-yB)*x-yB*xA+yA*xB=0; sinon m:=L; retourne y-m*(x-xA)-yA=0; fsi; ffonction:; fonction Cercle(cA,L) local cB,xA,xB,xM,yA,yB,yM,cM,r; xA:=cA[0]; yA:=cA[1]; si type(L)==DOM_LIST alors xB:=L[0]; yB:=L[1]; cB:=[xB,yB] cM:=Milieu(cA,cB); xM:=cM[0]; yM:=cM[1]; r:=Longueur(cA,cB); retourne [cM,r/2,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:; 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 "infinité de solution" sinon retourne "pas de solution" fsi; fsi; si degree(Eq,Var)==1 alors b:=subst(Eq,Var=0);a:=subst(Eq,Var=1)-b; retourne normal([-b/a]); fsi; si degree(Eq,Var)==2 alors 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 [(-b+sqrt(d))/(2*a),(-b-sqrt(d))/(2*a)];fsi; si d==0 alors retourne [-b/(2*a)]; fsi; retourne []; fsi; retourne "degree >2"; ffonction:; 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 simplify([Droite(cA,[xM1,yM1]),Droite(cA,[xM2,yM2])]); fsi; si l==r alors si (yI-yA)!=0 alors m:=-(xA-xI)/(yA-yI); retourne simplify(Droite([xA,yA],m)); sinon retourne Droite([xA,yA],[xA,yA+1])]); fsi; fsi; retourne []; ffonction:;
Chapitre 6 Quelques tests géométriques
6.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ésEstaligne(A,B,C) renvoie 0
On tape :
fonction Estaligne(A,B,C) local xA,yA,xB,yB,xC,yC; si A==B et A==C alors retourne 2; fsi; si A==B ou A==C alors retourne 1; fsi; xA:=A[0]; yA:=A[1]; xB:=B[0]; yB:=B[1]; xC:=C[0]; yC:=C[1]; si (xB-xA)*(yC-yA)==(xC-xA)*(yB-yA) alors retourne 1; fsi; retourne 0; ffonction:;
6.2 Test de parallélisme de 2 droites
Soient deux droites D1 et D2 d’équation :
a1x+b1y+c1=0 et a2x+b2y+c2=0
Ces 2 droites sont parallèles si a1b2=a2b1.
On rappelle la fonction Coeffdroite qui calcule les
coefficients a, b et c de
l’équation d’une droite ax+by+c=0 :
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 a,b,c; ffonction:;
On tape :
fonction Estparallele(d1,d2) local a1,a2,b1,b2,gd1,gd2,d,c1,c2; gd1(x,y):=gauche(d1); gd2(x,y):=gauche(d2); a1,b1,c1:=Coeffdroite(d1); a2,b2,c2:=Coeffdroite(d2); d:=a2*b1-b2*a1; si d==0 alors retourne 1; sinon retourne 0; fsi; ffonction:;
6.3 Caractériser alignement et parallélisme par la colinéarité
Trois points A, B et C sont alignés si les vecteurs
AB et AC sont colinéaires.
Deux droites AB et CD sont parallèles si les vecteurs AB et
CD sont colinéaires.
Chapitre 7 Statistiques
7.1 Calcul de moyenne et écart-type
Exercice : Écrire une fonction prenant en argument une liste des données
et renvoyant la moyenne et l’écart-type, au
moyen d’une boucle pour
(sans utiliser mean
ou stddev
).
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:;
Exercice : même question mais avec en argument valeurs/effectifs (soit avec une seule liste alternant valeur et effectif, soit avec deux listes).
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 sort
, puis on renvoie l’élément
d’indice la taille de la liste/2.
7.2 Simulation d’un échantillon
Exercice : générer
aléatoirement n valeurs 0 ou 1,
la probabilité d’avoir 1 étant fixée à p
(pour faire cela, on comparera avec p
le résultat de la commande 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:;
Commandes Xcas permettant de faire le calcul directement :
(la commande randvector
génère ici n valeurs aléatoires
selon la loi binomiale de paramètres 1 et p, on en fait ensuite
la moyenne)
7.3 Intervalle de fluctuation
Écrire un algorithme effectuant N 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 [p−1/√n,p+1/√n].
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:;
Commande Xcas correspondante :
(On génère une séquence de N moyennes de vecteurs de n
valeurs aléatoires selon la loi binomiale de paramètres 1 et p)
(On compte les moyennes dont l’écart à p est plus grand que 1/√n).
Visualisation de la répartition des fréquences
La probabilité d’être dans l’intervalle [p−1/√n,p+1/√n]
est donnée par
Chapitre 8 Aide
8.1 Les fonctions usuelles avec Xcas
abs
cos
evalf
floor
frac
max
min
round
sin
sqrt
tan
8.2 Les fonctions Xcas de calcul formel utilisées
coeff
degree
dim
droit
evalf
gauche
iquo
iquorem
irem
normal
purge
subst
8.3 Les fonctions Xcas de géométrie utilisées
carre
cercle
droite
equation
point
polygone
polygone_ouvert
segment
triangle
8.4 Les fonctions Xcas de programmation utilisées
fpour
fsi
ftantque
jusque
local
pas
pour <k> de <k1> <k2> faire <instructions> pas <p> fpour
quand
retourne
si <condition> alors <instructions1> sinon <instructions2> fsi
tantque <condition> faire <instructions> ftantque
8.5 Les fonctions Xcas utilisées en statistiques
alea
binomial
count
ranv
plotlist
Annexe A Les biais des langages interprétés
Ce chapitre est destiné aux utilisateurs maitrisant bien le langage de Xcas ou tout langage équivalent et qui veulent aller plus loin.
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 1, 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). 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 à 263 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
. 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és dans des régistres
du microprocesseurs. 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 j-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 n 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 M par un vecteur v, on peut optimiser le temps d’exécution en mutualisant la lecture des coefficients de v, on fait plusieurs produits scalaires avec v 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.