Algorithmes de calcul formel et numériqueB. Parisse |
Giac/Xcas est un logiciel libre de calcul formel dont
une caractéristique est de nécessiter peu de ressources sans
sacrifier les performances (en particulier sur les calculs polynomiaux).
Ce document décrit une partie des algorithmes de calcul formel et
numérique qui y sont
impleémentés, l’objectif à long terme
est de couvrir l’essentiel des algorithmes implémentés.
Ce n’est pas le manuel d’utilisation de Xcas, ni un manuel de programmation
ou d’exercices illustrés avec Xcas (voir le menu Aide, Manuels :
Référence calcul formel, Programmation, Exercices, Amusements...).
Ce texte regroupe donc des résultats
mathématiques qui ont été ou sont utilisés dans Giac
(ou sont susceptibles de l’être),
ils sont en général accompagnés de preuves
et souvent d’illustrations avec Xcas.
Pour plus d’informations sur Giac/Xcas, cf. :
www-fourier.ujf-grenoble.fr/~parisse/giac_fr.html
N.B.: La version HTML de ce document comporte des champs de saisie
interactifs, ceux-ci apparaissent comme des commandes “mortes” dans
la version PDF (elles sont exécutées une fois pour toutes
par la version non interactive de giac
).
La version HTML est optimisée pour le navigateur Firefox.
Elle est générée avec hevea.inria.fr
de Luc Maranget,
ou le fork de Yannick Chevallier pour le support mathjax, ainsi
qu’une version modifiée de itex2MML
de Jacques Distler pour la conversion en MathML.
Si vous avez une machine très puissante, vous pouvez
exécuter toutes les commandes interactives en cliquant sur le bouton
Exécuter. En-dessous de ce bouton se trouve
la console de l’interpréteur du
logiciel de calcul formel.
Table des matières
- Chapitre 1 Plan et index
- Chapitre 2 Trousse de survie Xcas
- Chapitre 3 Calculer sur ordinateur
- 3.1 Représentation des entiers
- 3.2 Les réels
- 3.3 L’arithmétique d’intervalle.
- 3.4 Calcul exact et approché, types, évaluation.
- 3.5 Forme normale et reconnaissance du 0.
- 3.6 Valeur générique des variables et hypothèses
- 3.7 Structures de données
- 3.8 Algorithmes et complexité.
- 3.9 Entiers et polynômes.
- 3.10 Euclide
- 3.11 Quelques algorithmes d’arithmétique de base.
- 3.12 Algorithmes rapides sur les polynômes en une variable.
- 3.13 Pour en savoir plus.
- 3.14 Exercices sur types, calcul exact et approché, algorithmes de bases
- Chapitre 4 Les générateurs de nombres pseudo-aléatoires.
- Chapitre 5 Le PGCD de polynômes.
- Chapitre 6 Le résultant
- Chapitre 7 Localisation des racines
- Chapitre 8 Exercices (PGCD, résultant, ...)
- Chapitre 9 Bases de Gröbner.
- Chapitre 10 Courbes paramétriques et polaires
- Chapitre 11 Propriétés métriques des courbes.
- Chapitre 12 Représentation des courbes implicites.
- Chapitre 13 Formes différentielles et intégrales curvilignes
- Chapitre 14 Équations et systèmes différentiels.
- 14.1 Introduction et représentation graphique.
- 14.2 Existence et unicité
- 14.3 Quelques méthodes de résolution explicite.
- 14.3.1 Équations à variables séparables
- 14.3.2 Équations linéaires
- 14.3.3 Équations linéaires à coefficients constants
- 14.3.4 Systèmes différentiels linéaires à coefficients constants d’ordre 1.
- 14.3.5 Systèmes et équations
- 14.3.6 Allure des courbes en dimension 2.
- 14.3.7 Systèmes d’ordre plus grand que 1
- 14.3.8 Intégrales premières.
- 14.3.9 Le modèle proie-prédateur
- 14.3.10 Quelques autres méthodes
- 14.4 Comportement asymptotique des solutions
- 14.5 Résolution numérique
- Chapitre 15 Introduction au calcul variationnel
- Chapitre 16 Corps finis.
- Chapitre 17 Factorisation des entiers et primalité.
- Chapitre 18 Factorisation des polynômes.
- 18.1 Les facteurs multiples
- 18.2 Factorisation en une variable
- 18.3 Factorisation à plusieurs variables
- 18.4 Preuve de l’identité de Bézout généralisée
- 18.5 Algorithme de Bézout généralisé
- 18.6 Factorisation rationnelle et sur une extension
- 18.7 Factorisation absolue
- 18.8 Compléments
- 18.9 Exercices (factorisation des polynômes)
- Chapitre 19 Intégration formelle.
- Chapitre 20 Intégration numérique
- Chapitre 21 Suites récurrentes et applications
- 21.1 Calcul de l’expression des suites récurrentes.
- 21.2 Le point fixe dans
- 21.3 Le point fixe dans
- 21.4 La méthode de Newton dans .
- 21.5 La méthode de Newton dans .
- 21.6 Vitesse de convergence, accélération de convergence.
- 21.7 Calcul approché des racines complexes simples
- 21.8 Méthodes de gradient (sans contrainte)
- Chapitre 22 Algèbre linéaire
- 22.1 Résolution de systèmes, calcul de déterminant.
- 22.2 Algèbre linéaire sur
- 22.3 Le pivot de Gauss numérique.
- 22.4 La méthode de factorisation
- 22.5 La factorisation de Cholesky
- 22.6 Conditionnement
- 22.7 Réduction des endomorphismes
- 22.7.1 Le polynôme minimal (Krylov)
- 22.7.2 Le polynôme caractéristique
- 22.7.3 La méthode de Hessenberg
- 22.7.4 Les traces et les identités de Newton
- 22.7.5 La méthode de Leverrier-Faddeev-Souriau
- 22.7.6 Les vecteurs propres simples.
- 22.7.7 La forme normale de Jordan
- 22.7.8 Exemple 1
- 22.7.9 Exemple 2
- 22.7.10 Le polynôme minimal par Faddeev
- 22.7.11 Formes normales rationnelles
- 22.7.12 Fonctions analytiques
- 22.8 Quelques autres algorithmes utiles
- 22.9 Quelques méthodes alternatives au pivot
- 22.10 Réduction approchée des endomorphismes
- 22.11 Factorisations de matrices
- 22.12 Quelques références
- 22.13 Exercices (algèbre linéaire)
- Chapitre 23 Approximation polynomiale
- Chapitre 24 Développement de Taylor, asymptotiques, séries entières, fonctions usuelles
- Chapitre 25 La transformée de Fourier discrète.
- Chapitre 26 Le rayonnement solaire.
- Chapitre 27 La moyenne arithmético-géométrique.
- Annexe A Bonus : le “making of” de Giac/Xcas
- Annexe B Quelques opinions.
Chapitre 1 Plan et index
L’index commence page suivante dans la version PDF.
Quelques conseils de lecture :
- Des aspects calcul numérique sont abordés dans les sections 3 (représentation des données), 20 (intégration numérique), 21 (point fixe, Newton), 22 (Gauss, LU, conditionnement, Schur...), 23 et 24 (interpolation, approximation polynomiale), 25 (transformée de Fourier discrète),
- Des aspects calcul exact sont abordés dans les sections 3 (représentation des données), 5 (PGCD), 6 (résultant), 7 (racines), 9 (bases de Gröbner), 16 (corps finis), 17 et 18 (factorisation des entiers et polynômes), 19 (calcul de primitives), 22 (algèbre linéaire), 27 (moyenne arithmético-géométrique),
- le lecteur physicien trouvera peut-être un intérêt aux chapitres tirés de mon cours de licence L2 courbes et équations différentielles pour la physique, sections 10, 11, 12, 13, ??, 15, ainsi que le calcul de la répartition du rayonnement solaire sur Terre 26
Index
|
|
Chapitre 2 Trousse de survie Xcas
Cette section peut être vue comme un tutoriel très abrégé pour rapidement prendre en main Xcas par des exemples au niveau fin de licence master de mathématique et préparation aux concours de recrutement d’enseignants. Le lecteur pourra consulter le tutoriel calcul formel (menu Xcas, Aide, Débuter en calcul formel, tutoriel) pour plus de détails ou/et à un niveau mathématique moins élevé.
2.1 Utilisation comme super-calculatrice
-
On saisit une ligne de commande, on tape sur la touche Entree pour
l’évaluer. On peut saisir plusieurs commandes sur une même ligne
en les séparant par
;
(affiche le résultat) ou:;
(n’affiche pas le résultat). La syntaxe respecte les priorités usuelles des opérateurs, attention il faut toujours saisir le signe*
pour effectuer une multiplication (sauf raccourcis dans lécriture de monômes comme2x
). Mettez des parenthèses en cas de doute.
- Si on entre des données exactes, les calculs sont exacts
on peut alors convertir en approché avecevalf
Si on entre au moins une donnée approchée (nombre avec point décimal), les calculs sont approchés
Les nombres flottants peuvent être saisis avec lécriture standard mantisse/exposant séparés pare
- On peut stocker des résultats dans des variables
pour les réutiliser ensuite
On peut effacer le contenu d’une variable avecpurge
, elle redevient alors libre (elle s’évalue en elle-même)
Les noms de variable peuvent contenir plusieurs lettres
- Les noms de commande et mots-clefs du logiciel sont en général affichés avec une couleur spécifique. Les menus permettent de trouver les commandes par thème (menus Outils dans Xcas pour les plus courantes, menu Graphe avec des assistants pour les représentations graphiques les plus courantes, menu Cmd pour un ensemble plus complet). La ligne de commande permet en général de compléter un début de nom de commande (touche tabulation). L’appui sur la touche tabulation après un nom de commande ou la sélection du menu Aide, Index dans Xcas ouvre l’aide en ligne courte, avec une description rapide de la commande et quelques exemples types que l’on peut copier-coller et adapter. On peut aussi obtenir une aide plus détaillée (boutons Details dans Xcas).
- Les listes sont délimitées par des crochets et servent
à représenter des vecteurs. Les listes de listes de même taille
servent à représenter des matrices. On accède à un élément
d’une liste en donnant le nom de variable de la liste puis l’indice
de l’élément entre crochet, en commençant à 0 (mettre deux crochets
pour commencer à 1).
- Pour afficher les étapes intermédiaires de certains calculs, exécutez
pour ne pas les afficher
2.2 Calcul exact
2.2.1 Arithmétique
-
division euclidienne des entiers
- PGCD, identité de Bézout sur les entiers
- restes chinois entiers
- primalité, décomposition en facteurs premiers
- puissance modulaire rapide
- division euclidienne des polynômes, la variable par défaut est ,
sinon il faut la spécifier en dernier argument
- PGCD, identité de Bézout sur les polynômes
- factorisation sur le corps des coefficients par défaut, sinon
ajouter l’extension algébrique souhaitée (éventuellement obtenue
avec
solve
)sqrfree
permet d’effectuer une factorisation partielle en produit de facteurs premiers entre eux et sans racines multiples. - évaluation d’un polynôme
- Résultant de 2 polynômes
- décomposition en éléments simples
- polynômes à coefficients dans
- Corps fini non premier de caractéristique :
GF(p,n)
crée un corps fini de cardinal , et affecte une variable pour le générateur du groupe multiplicatif , par défaut . Les éléments du corps sont alors représentés par des polynômes en le générateur
on peut travailler avec des polynômes ou des matrices à coefficients dans
2.2.2 Algèbre linéaire exacte
-
Pivot de Gauss :
rref
crée des 0 de part et d’autre de la diaginale,ref
en-dessous de la diagonale,ker
renvoie une liste de vecteurs formant une base du noyau d’une application linéaire - Factorisation LU
- Déterminant
On peut forcer l’utilisation d’un algorithme (voir l’aide détaillée dedet
). - Réduction des endomorphismes
- Polynome d’endomorphisme
- Forme de Hermite et Smith d’une matrice à coefficients entiers
2.3 Calcul scientifique
2.3.1 Analyse numérique
-
Résolution approché déquation par méthode itérative
ou par bisection
- Approximation polynômiale :
- Intégration numérique
on peut forcer une méthode avecgaussquad
ouromberg
ou de petit ordre avecplotarea
- Équations différentielles ordinaires
avec condition initiale , valeur en
Tracé sur l’intervalle
2.3.2 Algèbre linéaire numérique
-
factorisation LU et résolution de système,
- factorisation QR
- factorisation de Cholesky
- Conditionnement d’une matrice pour la norme 1, euclidienne ou infinie
- Réduction des endomorphismes
- Valeurs singulières
- Factorisation de Schur
Chapitre 3 Calculer sur ordinateur
3.1 Représentation des entiers
Preuve : On prend pour le plus grand entier tel que .
Exemple :
La division euclidienne permet d’écrire un nombre entier, en utilisant
une base et des caractères pour représenter les entiers
entre 0 et . Nous écrivons les nombres entiers en base
avec comme caractères les chiffres de 0 à 9.
Les ordinateurs utilisent des circuits binaires pour stocker
les informations, il est donc naturel d’y travailler en base 2
en utilisant comme caractères 0 et 1 ou en base 16 en utilisant
comme caractères les chiffres de 0 à 9 et les lettres de A à F.
En général, pour trouver l’écriture d’un nombre en base (par
exemple ),
on effectue des divisions euclidienne successives par du nombre puis
de ses quotients successifs jusqu’à ce que le quotient soit 0 et on
accolle les restes obtenus (premier reste à droite, dernier
reste à gauche).
Inversement, pour retrouver un entier à partir
de son écriture , on traduit les divisions euclidiennes
successives en
Par exemple, vingt-cinq s’écrit en base 16 0x19
car 25 divisé
par 16 donne quotient 1, reste 9
En base 2, on trouverait 0b11001
car .
On peut effectuer les opérations arithmétiques de base
(+,-,*, division) directement en base 2 (ou 16). Par exemple
la table de l’addition est 0+0=0, 0+1=1+0=1 et 1+1=0 je retiens 1,
donc :
01001111 + 01101011 ---------- 10111010
Exercice : comment passe-t-on simplement de la représentation d’un nombre en base 2 à un nombre en base 16 et réciproquement ?
Les microprocesseurs peuvent effectuer directement les opérations arithmétiques de base sur les entiers “machine” (déclinés en plusieurs variantes selon la taille et la possibilité d’avoir un signe). Noter que la division de deux entiers et n’a pas la même signification que la division de deux réels, comme elle ne tomberait pas forcément juste, on calcule le quotient et le reste de la division euclidienne.
Ces entiers machines permettent de représenter de manière exacte des petits entiers relatifs par exemple un entier machine signé sur 4 octets est compris entre .
Ces entiers machines permettent de faire très rapidement du calcul exact sur les entiers, mais à condition qu’il n’y ait pas de dépassement de capacité, par exemple pour des entiers 32 bits, renverra 0. Ils sont utilisables avec tous les langages de programmation traditionnels.
Les logiciels de calcul formel et certains logiciels de programmation permettent de travailler avec des entiers de taille beaucoup plus grande, ainsi qu’avec des rationnels, permettant de faire du calcul exact, mais on paie cette exactitude par un temps de calcul plus long, de plus pas mal de méthodes numériques ne gagnent rien à faire des calculs intermédiaires exacts. Néanmoins, l’utilisation d’un logiciel de calcul formel permettra dans certains cas d’illustrer certains phénomènes dus au calcul approché.
3.2 Les réels
On se ramène d’abord au cas des réels positifs, en machine on garde traditionnellement un bit pour stocker le signe du réel à représenter.
3.2.1 Virgule fixe et flottante.
La première idée qui vient naturellement serait d’utiliser
un entier et de déplacer la virgule
d’un nombre fixe de position, ce qui revient à mulitplier
par une puissance (négative) de la base. Par exemple en base 10 avec un
décalage de 4, 1234.5678
serait représenté par 12345678
et 1.2345678
par
12345
(on passe de l’entier au réel par multiplication
par . L’inconvénient d’une telle représentation est
qu’on ne peut pas représenter des réels grands ou petits,
comme par exemple le nombre d’Avogadro, la constante de Planck, etc.
D’où l’idée de ne pas fixer la position de la virgule, on parle
alors de représentation à virgule flottante ou de nombre flottant : on
représente un nombre par deux entier, l’un appelé mantisse
reprend les chiffres significatifs du réel sans virgule, l’autre
l’exposant, donne la position de la virgule. Attention, le séparateur
est un point et non une virgule dans la grande
majorité des logiciels scientifiques.
On sépare
traditionnellement la mantisse de l’exposant par la lettre e
.
Par exemple 1234.5678
peut être représenté
par 12345678e-8
(mantisse 12345678
, exposant -8)
mais aussi par 1234567800e-10
.
Naturellement, sur un ordinateur, il y a des limites pour les entiers représentant la mantisse et l’exposant . Si on écrit les nombres en base , la mantisse s’écrira avec un nombre fixé de chiffres (ou de bits en base 2), donc . Soit un réel représenté par Si , alors on peut aussi écrire avec , quelle écriture faut-il choisir? Intuitivement, on sent qu’il vaut mieux prendre le plus grand possible, car cela augmente le nombre de chiffres significatifs (alors que des 0 au début de ne sont pas significatifs). Ceci est confirmé par le calcul de l’erreur d’arrondi pour représenter un réel. En effet, si est un réel non nul, il ne s’écrit pas forcément sous la forme , on doit l’arrondir, par exemple au plus proche réel de la forme . La distance de à ce réel est inférieure ou égale à la moitié de la distance entre deux flottants consécutifs, et , donc l’erreur d’arrondi est inférieure ou égale à . Si on divise par , on obtient une erreur relative d’arrondi majorée par . On a donc intérêt à prendre le plus grand possible pour minimiser cette erreur. Quitte à mulitplier par , on peut toujours se ramener (sauf exceptions, cf. ci-dessous), à , on a alors une erreur d’arrondi relative majorée par
On appelle flottant normalisé un flottant tel que . Pour écrire un réel sous forme de flottant normalisé, on écrit le réel en base , et on déplace la virgule pour avoir exactement chiffres non nuls avant la virgule et on arrondit (par exemple au plus proche). L’exposant est égal au décalage effectué. Notez qu’en base 2, un flottant normalisé commence forcément par 1, ce qui permet d’économiser un bit dans le stockage.
Ainsi, l’erreur d’arrondi commise lorsqu’on représente un réel (connu exactement) par un double normalisé est une erreur relative inférieure à de ( et pour les doubles).
Exemples :
-
en base 10 avec , pour représenter
, on doit décaler la virgule de 5 positions,
on obtient
314159.265...
on arrondit à donc on obtient314159e-5
. - en base 2 avec , pour représenter trois cinquièmes (
en base 10, noté en base 2),
on pose la division en base 2 de
11
par101
, ce qui donne11 | 101 110 --------- -101 | 0.1001 ---- | 010 | 100 | 1000 | - 101 | ----- | 011 |
on retrouve le nombre de départ donc le développement est périodique et vaut0.1001 1001 1001 ...
. On décale le point de 10 positions, on arrondit, donc trois cinquièmes est représenté par la mantisse1001100110
et l’exposant-10
. On observe aussi sur cet exemple que dont l’écriture en base 100.6
est exacte, n’a pas d’écriture exacte en base 2 (de même que 1/3 n’a pas d’écriture exacte en base 10).
Il existe une exception à la possibilité de normaliser les flottants, lorsqu’on atteint la limite inférieure de l’exposant . Soit en effet le plus petit exposant des flottants normalisés et considérons les flottants et . Ces flottants sont distincts, mais leur différence n’est plus représentable par un flottant normalisé. Comme on ne souhaite pas représenter par 0, (puisque le test renvoie faux), on introduit les flottants dénormalisés , il s’agit de flottants dont l’exposant est l’exposant minimal représentable sur machine et dont la mantisse appartient à . Par exemple 0 est représenté par un flottant dénormalisé de mantisse 0 (en fait 0 a deux reprśentation, une de signe positif et une de signe négatif).
Enfin, on utilise traditionnellement une valeur de l’exposant pour représenter les nombres plus grands que le plus grand réel reprśentable sur machine (traditionnellement appelé plus ou moins infini) et les erreurs (par exemple 0./0. ou racine carrée d’un nombre réel négatif, traditionnellement appelé NaN, Not a Number).
Exercice : quels sont les nombres réels représentables exactement en base 10 mais pas en base 2 ? Si on écrit en base 2 avec 53 bits de précision, puis que l’on arrondit avec 64 bits de précision, ou si on écrit en base 2 avec 64 bits de précision, obtient-on la même chose ?
Les ordinateurs reprśentent généralement les flottants en base 2
(cf. la section suivante pour
plus de précisions), mais cette représentation n’est pas utilisée
habituellement par les humains, qui préfèrent compter
en base 10. Les ordinateurs effectuent donc la conversion dans
les routines d’entrée-sortie. Le format standard utilisé
pour saisir ou afficher un nombre flottant dans un logiciel
scientifique est composé d’un nombre à virgule
flottante utilisant le point comme séparateur décimal (et
non la virgule) suivi si nécessaire de la lettre e
puis de l’exposant,
par exemple 1.23e-5
ou 0.0000123
. Dans les
logiciels de calcul formel, pour distinguer un entiers
représentés par un entier
d’un entier représenté par un flottant on écrit
l’entier suivi de .0
par exemple 23.0
.
Remarque :
Les microprocesseurs ayant un mode BCD peuvent avoir un format
de représentation des flottants en base 10, les nombres décimaux
comme par exemple 0.3 peuvent être représentés exactement.
Certains logiciels, notamment maple, utilisent par défaut des
flottants logiciels en base 10 sur des microprocesseurs sans mode BCD,
ce qui entraine une baisse de
rapidité importante pour les calculs numériques (on peut
partiellement améliorer les performances en utilisant evalhf
en maple).
3.2.2 Les flottants au format double
Cette section développe les notions de la section précédente pour les flottants machine selon la norme IEEE-754, utilisables dans les langage de programmation usuels, elle peut être omise en première lecture. La représentation d’un double en mémoire se compose de 3 parties : le bit de signe sur 1 bit, la mantisse sur 52 bits, et l’exposant sur 11 bits. Pour les nombres “normaux”, l’exposant est en fait compris entre 1 et , le nombre représenté est le rationnel Pour écrire un nombre sous cette forme, il faut d’abord chercher par quel multiple de 2 il faut le diviser pour obtenir un réel dans , ce qui permet de déterminer l’exposant . Ensuite on écrit la représentation en base 2 de . Exemples :
-
-2
Signe négatif. Il faut diviser sa valeur absolue 2 par pour être entre 1 et 2 dont , l’exposant est . On a alors , . Représentation
1 10000000000 00000000...0000
- 1.5=3/2
Signe positif, compris entre 1 et 2 dont l’exposant vérifie soit . On a . D’où la représentation
0 01111111111 10000000...0000
- 6.4=32/5
Positif. Il faut le diviser par pour avoir donc soit . Ensuite qu’il faut écrire en base 2 (cf. section précédente), on écrit donc les 52 premiers éléments du développement avec une règle d’arrondi du dernier bit au nombre le plus proche. Ici le bit suivant le dernier1001
est un1
, on arrondit donc à1010
. D’où la représentation
0 1000000001 100110011001...10011010
On observe que la représentation en base 2 de 6.4 a du être
arrondie (car elle est infinie en base 2) bien qu’elle soit exacte
(finie) en base 10.
Seuls les entiers et les rationnels dont le dénominateur est une puissance
de 2 peuvent être représentés exactement.
Ceci entraine des résultats qui peuvent surprendre
comme par exemple le fait que
0.5 - 5*0.1
n’est pas nul.
Des représentations spéciales (avec ou ) ont été introduites pour représenter (pour les flottants plus grands en valeur absolue que le plus grand flottant représentable), et pour représenter les nombres non nuls plus petits que le plus petit flottant représentable de la manière exposée ci-dessus (on parle de flottants dénormalisés), ainsi que le nombre NaN (Not a Number) lorsqu’une opération a un résultat indéfini (par exemple 0/0).
Remarque : Sur les processeurs compatibles avec les i386,
le coprocesseur arithmétique i387 gère en interne des flottants
avec 80 bits dont 64 bits de mantisse. Sur les architectures 64 bits
(x86 ou AMD), le jeu d’instruction SSE permet de travailler avec
des flottants de 128 bits. Le compilateur gcc permet d’utiliser
ces flottants longs avec le type long double
ou
les types __float80
et __float128
en utilisant
un drapeau de compilation du type -msse
3.2.3 Opérations sur les flottants
Les opérations arithmétiques de base sur les flottants se font de la manière suivante :
- addition et soustraction : on détecte s’il faut additionner ou soustraire en valeur absolue en analysant les signes, on détermine l’exposant le plus grand et on décale la partie mantisse du flottant dont l’exposant est le plus petit pour se ramener à additionner deux entiers (partie mantisses correspondant au même exposant), on décale à nouveau la partie mantisse en modifiant l’exposant après l’opération pour normaliser le flottant
- multiplication : on additionne les exposants et on multiplie les parties mantisses (vus comme des entiers), on arrondit et on ajuste l’exposant si nécessaire
- division : on soustrait les exposants et on divise les parties mantisses (division “à virgule”), on tronque et on ajuste l’exposant si nécessaire
3.2.4 Erreurs
La représentation des nombres réels par des doubles présente
des avantages, les opérations arithmétiques
sont faites au plus vite par le microprocesseur.
Les coprocesseurs arithmétiques (intégrés sur les microprocesseurs
de PC) proposent même
le calcul des fonctions usuelles (trigonométriques, racine carrée, log et exp)
sur le type double et utilisent des formats de représentation interne
ayant plus de 64 bits pour les doubles, ce qui permet de limiter
les erreurs d’arrondi.
Par contre, des erreurs vont être introduites,
on parle de calcul approché par opposition au calcul exact sur les
rationnels. En effet, la représentation doit d’abord arrondir
tout réel qui n’est pas un rationnel dont le dénominateur
est une puissance de 2. Ensuite chaque opération va entrainer
une propagation de ces erreurs et va y ajouter une erreur d’arrondi
sur le résultat.
Enfin, l’utilisation du type double peut provoquer un dépassement
de capacité (par exemple 100!*100!
).
Pour diminuer ces erreurs et les risques de dépassement de capacité, il existe des types flottants multiple précision, qui permettent de travailler avec un nombre fixé à l’avance de décimales et une plage d’exposants plus grande. Les calculs sont plus longs mais les erreurs plus faibles. Attention, il s’agit toujours de calcul approché! De plus, pour des quantités dont la valeur est déterminée de manière expérimentale, la source principale de propagation d’erreurs est la précision des quantités initiales, il ne sert souvent à rien d’utiliser des types flottants multiprécision car les erreurs dus à la représentation (double) sont négligeables devant les erreurs de mesure. Dans ce cas, il est pertinent lorsqu’on évalue avec mal connu de calculer aussi , en effet : l’erreur relative sur est donc au premier ordre multipliée par Par exemple, l’erreur relative sur est au premier ordre l’erreur relative sur multipliée par .
3.2.5 Erreur absolue, relative, arrondi propagation des erreurs.
On a vu précédemment que pour représenter un réel, on devait l’arrondir, ce qui introduit une erreur même si le réel est connu exactement (par exemple 1/10). Voyons comment se propagent les erreurs dans les opérations arithmétiques de base : on distingue l’addition, la multiplication et l’inversion. La soustraction se ramène à l’addition car le calcul de l’opposé n’introduit aucune erreur nouvelle. Pour l’addition, si et si alors par l’inégalité triangulaire (), on a : on dit que les erreurs absolues s’additionnent.
Mais comme il faut représenter en machine, on doit ajouter une erreur d’arrondi, qui est proportionnelle à la valeur absolue de d’où la notion d’erreur relative :
Remarquons au passage que les erreurs de mesure expérimentales sont pratiquement toujours des erreurs relatives.
Donc lorsqu’on effectue une addition (ou une soustraction) de deux réels sur machine, on doit additionner les deux erreurs absolues sur les opérandes et ajouter une erreur d’arrondi (relative de , à titre d’exercice, on pourra vérifier que cette erreur d’arrondi est majorée par l’erreur absolue de la somme dès l’instant où et ont eux-même une erreur d’arrondi).
Lorsqu’on effectue une multiplication de deux nombres dont les représentants sont non nuls, on a l’erreur relative est donc la somme des erreurs relatives et du produit des erreurs relatives (on peut souvent négliger le produit devant la somme). Il faut aussi y ajouter une erreur relative d’arrondi de sur .
On observe que la multiplication est une opération posant moins de problèmes que l’addition, car on manipule toujours des erreurs relatives, par exemple si l’erreur relative sur deux doubles et non nuls est de , alors l’erreur relative sur sera de Lorsque l’erreur relative sur les données est grande devant , l’erreur relative d’arrondi final est négligeable, on peut alors dire que les erreurs relatives s’additionnent pour un produit (c’est aussi vrai pour un quotient: exercice!). Par contre, si on additionne deux nombres dont le représentant de la somme est proche de 0, la somme des erreurs absolues peut devenir non négligeable par rapport à la somme des représentants, entrainant une erreur relative très grande. Par exemple si est représenté par avec une erreur d’arrondi de et par avec la même erreur d’arrondi, l’addition de et renvoie avec une erreur absolue de (ici il n’y a pas d’arrondi lorsqu’on fait la somme). C’est une erreur relative de (qui domine largement l’erreur d’arrondi) ce qui signifie que dans la mantisse, seul le premier bit sur les 52 a un sens, la perte de précision est très grande.
Une autre conséquence importante est que l’addition de réels sur machine n’est pas une opération associative, par exemple alors que Dans Xcas, il n’y a que 48 bits de mantisse :
Exercice : pour calculer la valeur numérique d’une dérivée de fonction, il vaut mieux calculer que car le terme d’erreur est en et non en . Attention toutefois à ne pas prendre trop petit, sinon en flottants et même si , l’erreur absolue sur est (au moins) d’ordre , donc l’erreur relative est d’ordre . Par exemple pour h=1e-8 le reste est en donc de l’ordre des erreurs d’arrondi mais l’erreur relative sur est d’ordre largement supérieure (en flottants double-précision). On choisira plutôt tel que soit proche de , donc de l’ordre de 1e-5, qui fournira une valeur approchée avec une erreur relative de l’ordre de 1e-10. Exemple : calcul de la dérivée numérique de en
Remarquons néanmoins que les erreurs calculées ici sont des majorations
des erreurs réelles (ou si on préfère l’erreur obtenue dans le pire
des cas), statistiquement les erreurs sur les résultats sont moindres,
par exemple si on effectue calculs susceptibles de provoquer
des erreurs indépendantes suivant une même loi d’espérance nulle, la moyenne des
erreurs divisée par l’écart-type de la loi
tend vers une loi normale centrée réduite. De manière plus
déterministe, on a l’inégalité de Bienaymé-Tchebyshev
où est la variable aléatoire somme des erreurs,
l’erreur et la variance de la somme erreurs
supposées indépendantes, cette probabilité tend vers 0 pour
grand si est d’ordre , et ne tend
pas vers 0 si est de l’ordre de .
Exemple : somme de nombres répartis sur selon la loi
uniforme (représentant des erreurs), on divise par 20,
on effectue plusieurs tirages (par exemple 500) on trace l’histogramme et
on compare avec la loi normale de moyenne
nulle (l’espérance de la somme) et d’écart-type celui de la loi
uniforme.
Attention, si on effectue la somme de réels , les erreurs d’arrondis ne satisfont pas à ces hypothèses. En effet, l’erreur d’arrondi à chaque opération est une erreur relative, l’erreur absolue correspondante est puis puis ... , que l’on peut majorer par La majoration de l’erreur d’arrondi dépend donc de l’ordre des termes, on a intérêt à sommer en commençant par les termes les plus petits en valeur absolue. Mais on peut faire mieux, il est possible de corriger les erreurs d’arrondi dans une somme avec le programme suivant pour une liste (on peut bien sur adapter à la somme d’une expression dépendant d’une variable entière sans stocker de liste) :
Somme(l):={ local x,s,c; s:=0.0; c:=0.0; pour x in l faire c += (x-((s+x)-s)); s += x; fpour; print(c); return s+c; }:;
onload
En effet, devrait valoir 0 sans erreurs d’arrondis,
avec les erreurs d’arrondis, on a le premier calcul qui donnera
une erreur opposée à celui du calcul de à la ligne
suivante, le 2ième calcul effectué donne une erreur
absolue en au pire (car c’est une erreur relative
par rapport à ),
et la 3ième erreur d’arrondi est négligeable
(puisque la somme vaut 0). On a donc une erreur absolue sur
qui est au premier ordre au pire en ,
bien meilleure que
la majoration
calculée précédemment.
Par exemple
à comparer avec
(le calcul de est fait en exact, celui de sum(1. /j,j,1,n)
est approché sans correction).
En conclusion, il est souvent très difficile de calculer une majoration rigoureuse de l’erreur pour des calculs (sauf les plus simples), et cette majoration est en général bien trop pessimiste. Lorsqu’on doute de la précision d’un calcul, un test peu couteux consiste à refaire ce calcul en utilisant des flottants en précision plus grande et tester si le résultat varie en fonction du nombre de chiffres significatifs utilisés, ou faire varier légèrement les données et observer la sensibilité du résultat. Si on veut travailler en toute rigueur sans pour autant calculer les erreurs à priori, il faut utiliser un logiciel utilisant des intervalles pour représenter les réels (section suivante)
3.3 L’arithmétique d’intervalle.
Certains systèmes de calcul formel peuvent manipuler directement
des intervalles réels, par exemple par l’intermédiaire de la
bibliothèque C MPFI. Les opérations arithmétiques sur des
intervalles renvoient alors le meilleur intervalle possible contenant
toutes les valeurs possibles lorsque les opérandes parcourent
leurs intervalles respectifs.
Exemple en Xcas (version 1.1.1 et ultérieures) :
[-1..2]*[-1..2]
renvoie [-2..4]
.
Attention ici on parcourt toutes les valeurs possibles de
. Ce qui est différent du carré
d’un intervalle ou plus généralement de l’évaluation
d’un polynôme en un intervalle, horner(x^2,[-1..2])
renvoie ainsi [0..4]
.
Les fonctions disponibles sont souvent moins riches qu’en arithmétique flottante, le calcul d’une fonction non monotone sur un intervalle peut s’avérer délicat, alors que si la fonction est monotone, il suffit de calculer l’image des deux bornes de l’intervalle. Pour les polynômes, Xcas décompose les coefficients en deux parties en fonction du signe, puis utilise la monotonie de et sur et respectivement.
L’arithmétique d’intervalle dans est beaucoup plus difficile à mettre en oeuvre puisqu’il n’y a plus d’ordre ni de monotonie, on doit alors s’en remettre à des estimations sur les parties réelles et imaginaires qui ne tiendront pas compte du phénomène ci-dessus sur la différence entre et .
3.4 Calcul exact et approché, types, évaluation.
Dans les langages de programmation traditionnel (C, Pascal,...), il existe déjà des types permettant une représentation exacte des données (type entier) ou une représentation approchée (type flottant). Mais ces types de donnée de base occupent une taille fixe en mémoire, le type entier est donc limité à un intervalle d’entiers (par exemple pour un entier non signé sur une machine utilisant un processeur 32 bits) alors que le type flottant peut représenter des nombres réels, mais est limité à une précision en nombre de digits de la mantisse et de l’exposant (par exemple 12 chiffres significatifs et un exposant compris entre -499 et 499).
En calcul formel, on souhaite pouvoir calculer rigoureusement d’une part, et avec des paramètres dont la valeur n’est pas connue d’autre part ; il faut donc s’affranchir de ces limites :
- pour les entiers relatifs, on utilise des entiers de précision arbitraire dont la taille en mémoire est dynamique (déterminée pendant l’exécution et non à la compilation),
- pour les nombres complexes, on utilise un couple de nombres réels,
- pour les rationnels, on utilise un couple d’entiers relatifs,
- pour les irrationnels algébriques (par exemple ), on utilise un polynôme irréductible dont ils sont racines,
- pour les paramètres (), on utilise un type structuré contenant un champ de type chaine de caractères pour représenter le nom du paramètre et un champ pour attribuer une valeur à (ou une hypothèse sur) ce paramètre,
- pour les nombres transcendants (par exemple ), on est obligé d’introduire un paramètre auquel on attribue une valeur numérique, qui ne sera utilisée qu’au moment où on veut une approximation numérique d’une expression contenant ce nombre transcendant, on parle de constante,
- lorsqu’on a besoin d’une approximation numérique d’un nombre, on peut utiliser des conversions de ces types en un type flottant. On peut aussi pour lutter contre les erreurs d’arrondi utiliser des nombres flottants étendus dont la précision est dynamique ou même des intervalles de flottants étendus,
- il faut aussi un nouveau type, appelé expression ou symbolique, permettant d’appliquer une fonction qu’on ne peut évaluer directement sur les objets précédents, par exemple . Il doit s’agir d’une opération de clôture, au sens où appliquer une fonction à un objet symbolique ne nécessite pas la création d’un nouveau type (en général on renvoie un objet symbolique).
Enfin, il faut pouvoir évaluer un objet (en particulier symbolique) :
par exemple évaluer lorsqu’on assigne une valeur à .
Dans cet exemple, on voit qu’il faut d’abord remplacer par
sa valeur avant de lui appliquer la fonction sinus. C’est le mécanisme
général de l’évaluation, mais il y a quelques exceptions où
on souhaite empêcher l’évaluation d’un ou plusieurs arguments
d’une fonction avant l’évaluation de la fonction. Par exemple si on
veut calculer la valeur numérique d’une intégrale par des méthodes
de quadrature, on ne souhaitera pas rechercher une primitive de la
fonction à intégrer. Dans le jargon, on parle alors de “quoter” un argument
(l’origine du terme vient probablement de la notation '
du langage
Lisp). Certaines fonctions doivent toujours quoter leurs arguments
(par exemple la fonction qui permet de purger le contenu d’un paramètre),
on parle parfois d’autoquotation.
3.5 Forme normale et reconnaissance du 0.
Une fois défini ces types de base représentant les nombres d’un système de calcul formel, il faut pouvoir comparer ces nombres, en particulier décider si deux représentations distinctes correspondent au même nombre ou, ce qui revient au même, par soustraction décider quand un nombre est nul. Par exemple et 2 représentent le même nombre. Lorsqu’on dispose d’un algorithme permettant de représenter un nombre d’une manière unique, on parle de forme normale. C’est par exemple le cas pour les nombres rationnels, la forme normale usuelle est la fraction irréductible de dénominateur positif. C’est aussi le cas pour les fractions rationnelles de polynômes à coefficients entiers représentées par une fraction irréductible, avec au dénominateur un coefficient de plus haut degré positif. Malheureusement, il n’est pas toujours possible de trouver une forme normale pour diverses raisons théoriques ou pratiques :
- on ne connaît pas toujours le statut de certaines constantes (par exemple la constante d’Euler),
- il n’existe pas d’algorithmes permettant de déterminer s’il existe des relations algébriques entre constantes,
- il n’existe pas forcément une seule forme plus simple, par exemple : Ce cas se présente fréquemment avec les extensions algébriques.
- en pratique il peut être trop coûteux d’utiliser une forme normale, par exemple le polynôme possède 1000 monômes
En résumé, au mieux on a une forme normale, au pire on risque de ne pas reconnaître un zéro, entre les deux on peut ne pas avoir de forme normale mais être capable de reconnaître à coup sûr une expression nulle (par contre, si le système de calcul formel détermine qu’une expression est nulle, alors elle l’est).
Il n’existe pas d’algorithme solution pour le problème de la reconnaissance du zéro pour une classe d’expressions "assez générale". Heureusement, dans la plupart des cas pratiques on sait résoudre ce problème, en se ramenant le plus souvent au cas des polynômes et fractions rationnelles. Par exemple, pour simplifier une expression trigonométrique, on remplace les fonctions trigonométriques par leur expression en fonction de , on est ainsi ramené à une fraction rationnelle en que l’on écrit sous forme normale.
Les polynômes ont un rôle central dans tout système de calcul formel
puisque sauf dans les cas les plus simples (fractions d’entiers par exemple),
la simplification d’expressions
fait appel à un moment ou à un autre à des calculs
de PGCD de polynômes. Le PGCD de polynômes est un algorithme
très sollicité auquel nous consacrerons une section. En effet,
l’application brutale de l’algorithme d’Euclide pose des problèmes
d’efficacité ce qui a obligé à inventer des méthodes plus efficaces.
Anticipons rapidement sur un exemple qui montre l’un des problèmes
majeurs des algorithmes de calcul formel, l’explosion en taille
(ici des coefficients des restes successifs).
Voici donc les restes successifs lorsqu’on applique l’algorithme
d’Euclide pour calculer le PGCD de avec
sa dérivée (les deux polynômes sont premiers entre eux) :
Le lecteur voulant tester d’autres exemples pourra utiliser le programme
Xcas
suivant :
pgcdderiv(a):={ local b,r,res; b:=diff(a,x); res:=NULL; for (;b!=0;){ res:=res,b; r:=rem(a,b); a:=b; b:=r; } return(res); }
3.6 Valeur générique des variables et hypothèses
Lorsqu’on utilise un symbole sans lui affecter de valeurs en mathématiques on s’attend à une discussion en fonction du paramètre représenté par ce symbole. Ce qui nécessiterait de créer un arborescence de calculs (on retrouve ici les problèmes d’explosion évoqués dans la section précédente). La plupart des systèmes de calcul formel contournent la difficulté en supposant que le paramètre possède une valeur générique (par exemple la solution de sera ) ou choisissent une branche pour les fonctions possédant un point de branchement (par exemple pour résoudre en fonction de ). Certains systèmes demandent de manière interactive à l’utilisateur si la variable est par exemple positive ou différente de 1 mais cela s’oppose à un traitement automatique. On peut aussi anticiper ce type de décision en faisant des hypothèses sur une paramètre, la plupart des systèmes de calcul formel actuel proposent cette possibilité.
3.7 Structures de données
On a vu plus haut qu’on souhaitait manipuler des entiers de taille non fixe, des réels de précision fixe ou non, des fractions, des nombres complexes, des extensions algébriques, des paramètres, des expressions symboliques. La plupart des systèmes proposent un type générique qui recouvre ces divers types de scalaire. On peut par exemple utiliser un type structuré comportant un champ type et la donnée ou un pointeur sur la donnée (avec dans ce cas un pointeur sur un compteur de références de la donnée pour pouvoir la détruire dès qu’elle n’est plus référencée1). En programmation orientée objet, on utiliserait plutôt un type abstrait dont dérivent ces différents scalaires et le polymorphisme.
Il faut aussi un type pour les vecteurs, les matrices et les listes. Il faut prendre garde à la méthode utilisée par le système lorsqu’on modifie un élément d’un vecteur, matrice ou liste : soit on effectue une copie de tout l’objet en modifiant l’élément, soit on modifie l’élément de l’objet original. La première méthode (par valeur) est plus aisée à comprendre pour un débutant mais la seconde méthode (par référence) est bien plus efficace.
On peut se poser la question de savoir s’il faut inclure
ces types dans le type générique ; en général la
réponse est affirmative, une des raisons étant que les
interpréteurs qui permettront de lire des données dans un
fichier texte sont en général basé sur le couple de logiciels
lex(flex)/yacc(bison)
qui ne peut compiler qu’à destination d’un
seul type. Ceci permet également d’unifier en un seul type symbolique
les fonctions ayant un ou plusieurs arguments en
voyant plusieurs arguments comme un vecteur d’arguments.
Les fonctions sont le plus souvent elle-même incluses dans le
type générique permettant ainsi à l’utilisateur de saisir des
commandes ou programmes fonctionnels (on peut
utiliser une fonction comme argument d’une commande).
Pour des raisons d’efficacité, les systèmes de calcul formel utilisent souvent des représentations particulières pour les polynômes dont on a dit qu’ils jouaient un rôle central. Pour les polynômes à une variable, on peut utiliser la liste des coefficients du polynôme, on parle alors de représentation dense. On peut aussi décider de ne stocker que les coefficients non nuls, on parle alors de représentation creuse (on stocke alors un couple formé par le coefficient et le degré du monôme correspondant). Pour les polynômes à plusieurs variables, on peut les considérer comme des polynômes à une variable à coefficients polynomiaux, on parle alors de représentation récursive. On peut aussi décider de ne pas briser la symétrie entre les variables (pas de variable principale), on parle alors de représentation distribuée, le plus souvent les représentation distribuées sont creuses car les représentations denses nécessitent très vite beaucoup de coefficients. Les méthodes de représentation creuses sont parfois aussi utilisées pour les matrices ayant beaucoup de coefficients nuls.
Voyons maintenant plus précisément sur quelques exemples de logiciels de calcul formel répandus quelles structures de données sont utilisées. Plusieurs éléments entrent en compte dans les choix faits :
- le(s) profil(s) d’utilisation (enseignement, ingéniérie, calcul intensif, recherche)
- les ressources disponibles (mémoire, puissance du processeur...)
- la facilité d’implémentation (choix du langage, outils disponibles en particulier débuggueurs, ...)
- l’histoire du système (un système conçu avec les outils disponibles aujourd’hui est forcément différent d’un système conçu il y a 20 ans)
Voyons quelques exemples, d’abord Giac, puis des systèmes pour ordinateur où les ressources (par exemple mémoire) sont moins limitées ce qui permet d’utiliser des langages de programmation de plus haut niveau. On termine par les calculatrices formelles HP et TI des années 20002. Ce sont des systèmes plutôt destinés à l’enseignement, soumis à de fortes contraintes en termes de taille mémoire, et destinés à traiter des petits problèmes.
3.7.1 Maple, Mathematica, ...
Ces systèmes ont un noyau fermé, au sens où l’utilisateur n’a pas accès du tout, ou en tout cas pas facilement, aux structures de données de base. Je ne dispose donc pas d’information sur les structures de données utilisées par le noyau.
L’interaction système-utilisateur se fait quasiment toujours en utilisant le langage de programmation propre au système, langage interprété par le noyau du système (ce qui ralentit l’exécution). Ces langages utilisateurs sont essentiellement non typés : on travaille avec des variables du type générique sans pouvoir accéder aux types sous-jacents. On ne bénéficie en général pas des vérifications faites lors de la compilation avec un langage typé, de plus ces systèmes ne sont pas toujours fourni avec de bon outils de mise au point. Enfin ces langages ne sont pas standardisés d’un système à l’autre et il est en général impossible d’utiliser ces systèmes comme des librairies depuis un langage de programmation traditionnel. Leur intérêt principal réside donc dans une utilisation interactive en profitant de la librairie de fonctions accessibles.
3.7.2 Giac/Xcas
Il s’agit du système de calcul formel que j’implémente actuellement sous
forme d’une bibliothèque C++ (ce qui
permettra aux programmes tiers d’utiliser beaucoup plus facilement du
calcul formel qu’avec les systèmes précédents). L’objectif est
d’avoir un système facile à programmer directement en C++, proche
du langage utilisateur, lui-même compatible avec Maple ou MuPAD,
tout cela sans trop perdre en performances comparativement aux
librairies spécialisées écrites en C/C++. Ce qui explique un choix
de type générique (gen
) non orienté objet, avec un champ type
et soit une donnée immédiate (pour les nombres flottants par exemple),
soit un pointeur vers un objet du type correspondant au champ type pour
les données de taille non fixe (on pourrait donc se
contenter du langage C, mais le langage C++ permet de redéfinir
les opérateurs sur des types utilisateurs ce qui
améliore considérablement la lisibilité du code source).
Les données dynamiques ne sont pas dupliquées, Giac
utilise un pointeur sur un compteur de référence pour détruire
ces données lorsqu’elles ne sont plus référencées.
Les entiers en précision arbitraire sont hérités de la bibliothque GMP (écrite en C) du projet GNU. Les flottants en précision arbitraire utiliseront aussi GMP (plus précisément MPFR). Il y a un type fraction, structure C composé d’un champ numérateur et d’un champ dénominateur, et un type nombre complexe.
Les listes, vecteurs, matrices utilisent le type paramétré vector<>
de la librairie standard C++ (Standard Template Library).
Les objets symboliques sont des structures composés d’un champ sommet
qui est une fonction prenant un argument de type gen
et renvoyant un résultat
de type gen
, et d’un champ feuille qui est de type gen
.
Lorsqu’une fonction possède plusieurs arguments, ils sont rassemblés
en une liste formant le champ feuille de l’objet symbolique.
Les programmes sont aussi des objets symboliques, dont le champ
sommet est la fonction évaluation d’un programme.
Les listes sont aussi utilisées pour représenter vecteurs, matrices
et polynômes en une variable en représentation dense, on peut
y accéder par valeur (:=
) ou par référence
(=<
). Ces polynômes servent eux-mêmes á représenter
des éléments d’une
extension algébrique de (vus comme
un couple de polynômes , où est un polynome minimal
irréductible à coefficients entiers, autrement dit vaut
où ),
ou des éléments d’un corps fini (comme ci-dessus, mais ici est
à coefficients dans avec premier, cf. la commande GF
).
Giac posséde aussi un type pour les polynômes
en représentation creuse distribuée en plusieurs
indéterminées (cf. les commandes symb2poly
et poly2symb
).
L’évaluation d’un objet symbolique se fait en regardant d’abord si la fonction au sommet doit évaluer ou non ses arguments (autoquote), on évalue les arguments si nécessaire puis on applique la fonction.
Une hypthèse sur un paramètre est une valeur spéciale affectée au paramètre, valeur ignorée par la routine d’évaluation.
3.7.3 Calculatrices formelles HP48/49
Les langages utilisés pour programmer ces calculateurs sont l’assembleur et le RPL (Reverse Polish Lisp) adapté à l’écriture de code en mémoire morte très compact.
Le type générique est implémenté avec un champ type appelé prologue (qui est en fait un pointeur sur la fonction chargée d’évaluer ce type d’objet) suivi de la donnée elle-même (et non d’un pointeur sur la donnée, on économise ainsi la place mémoire du compteur de référence).
Le type entier en précision arbitraire est codé par le nombre de digits (sur 5 quartets3) suivi du signe sur un quartet et de la représentation BCD (en base 10) de la valeur absolue de l’entier. Le choix de la représentation BCD a été fait pour optimiser les temps de conversion en chaîne de caractères pour l’affichage. La mémoire vive disponible est de 256K, c’est elle qui limite la taille des entiers et non le champ longueur de l’entier. Il n’y a pas de type spécifique pour les rationnels (on utilise un objet symbolique normal).
Les fonctions internes des HP49/50/40 utilisent le type programme pour représenter les entiers de Gauß (complexes dont la partie réelle et imaginaire est entière). Les nombres algébriques ne sont pas implémentés, sauf les racines carrées (représentée de manière interne par le type programme). Il y a un type spécifique prévu pour les flottants en précision arbitraire, mais l’implémentation des opérations sur ces types n’a pas été intégrée en ROM à ce jour.
Les types listes, programmes et objet symbolique sont composés du prologue
(champ type) suivi par la succession d’objets situés en
mémoire vive ou de pointeurs sur des objets situés en mémoire en lecture
seule (ROM) et se terminent par un pointeur sur une
adresse fixe (appelée SEMI
). Ces types sont eux-mêmes des
objets et peuvent donc être utilisés de manière
récursive. La longueur des types listes, programmes, symboliques
n’est stockée nulle part, c’est le délimiteur final
qui permet de la connaître, ce qui est parfois source d’inefficacité.
On utilise de manière interne les listes pour représenter les
polynômes denses (avec
représentation récursive pour les polynômes à plusieurs variables).
Les calculatrices HP4xG utilisent une pile4, c’est-à-dire une liste
de taille non fixée d’objets. On place les objets sur la pile,
l’exécution d’une fonction prend ces arguments sur
la pile et renvoie un ou plusieurs résultats sur la pile (ce qui est
une souplesse du RPN comparé aux langages où on ne peut renvoyer
qu’une valeur de retour). Il faut donc
donner les arguments avant d’appeler la fonction correspondante. Par
exemple pour calculer on tapera a b +
. C’est
la syntaxe dite polonaise inversée (RPN). Un avantage de cette syntaxe
est que le codage d’un objet symbolique par cette syntaxe est évidente,
il suffit de stocker la liste précédente {a b +}
.
Les objets symboliques sont donc représenté par une suite d’objets écrit
en syntaxe polonaise inversée. L’évaluation d’un objet symbolique se fait
dans l’ordre polonaise inversé : les arguments sont évalués
puis les fonctions leur sont appliqués. Pour des raisons d’efficacité,
on représente souvent les objets composites (listes, symboliques) par
leurs composants placés sur la pile (appelé meta-objets).
Une rigidité de la syntaxe polonaise est
que les fonctions ont toujours un nombre fixe d’arguments5, par
exemple l’addition a toujours 2 arguments, ainsi
est obtenu par ou par
c’est-à-dire respectivement a b + c +
ou a b c + +
ce qui
brise parfois artificiellement la symétrie de certaines opérations. En
polonaise inversée, le système doit de plus jongler avec l’autoquote puisque
les arguments sont évalués avant l’opérateur qui éventuellement demanderait
à ne pas évaluer ses arguments. À noter l’existence d’une commande
QUOTE
permettant à l’utilisateur de quoter une sous-expression.
Les hypothèses sur des variables réelles sont regroupées dans une liste
stockée dans la variable globale REALASSUME
, on peut supposer
qu’une variable est dans un intervalle. Il n’y a pas à ce jour
de possibilité de supposer qu’une variable est entière (ni à fortiori
qu’une variable à une valeur modulo un entier fixé), bien qu’il ait été
décidé de réserver la variable globale INTEGERASSUME
à cet effet.
Il n’y a pas de possibilité de faire des hypothèses ayant une portée
locale.
3.7.4 Calculatrices formelles TI92/89/Voyage 200
Le langage utilisé pour programmer ces calculatrices est le langage C (on peut aussi écrire du code en assembleur pour ces calculatrices). On retrouve ici les différents types de données regroupé en un type générique qui est un tableau d’octets (aussi appelé quantum). Le champ type est appelé tag dans la documentation TI. Contrairement à ce qui précède, ce champ type est placé en mémoire à la fin de l’objet, ce qui est possible car la longueur d’un objet est toujours indiquée au début de l’objet. Ceci est fait afin de faciliter l’évaluation (cf. infra).
Les entiers en précision arbitraire sont codés par un tag parmi deux (pour différencier le signe), un octet pour la longueur, puis la valeur absolue de l’entier (en base 256). Ils sont donc limités par le champ longueur à 255 octets, le plus grand entier représentable est 6 . Il existe un tag spécifique pour les rationnels, pour les constantes réelles et entières qui apparaissent par exemple en résolvant une équation. Il existe des tags utilisés de manière interne, par exemple pour les nombres complexes. Il n’y a pas de tag prévu pour les flottants en précision arbitraire. ni pour les nombres algébriques (racines carrées par exemple).
Les listes sont codées par la succession de leurs éléments. En principe elles ne peuvent pas contenir des listes (sauf pour représenter une matrice). Quelques fonctions utilisent les listes pour représenter des polynômes denses à une variable, mais probablement pas pour représenter de manière récursive des polynômes à plusieurs variables (puisque le type liste n’est en principe pas récursif).
Comme les HP, les TI utilisent une pile (non visible par l’utilisateur) appelée expression stack afin de traduire un expression mathématique sous forme d’un texte en un objet symbolique codé exactement comme ci-dessus en syntaxe polonaise. Toutefois, la présence du champ longueur permet d’évaluer un objet symbolique sans perdre en efficacité en partant de l’opérateur final et en redescendant ensuite sur ces arguments, c’est la stratégie adoptée. C’est pour cela que le tag d’identification se trouve à la fin de l’objet. L’utilisation de cette méthode facilite grandement l’autoquotation (on peut toutefois regretter que le système n’ait pas prévu d’instruction permettant à l’utilisateur d’empêcher l’évaluation d’une sous-expression).
On ne peut pas faire d’hypothèse globale sur un paramètre par contre on peut faire des hypothèses de type appartenance à un intervalle ayant une portée locale.
3.8 Algorithmes et complexité.
On va présenter dans la suite quelques algorithmes que l’on peut considérer comme classiques dans le domaine du calcul formel. Avant d’implémenter ce type d’algorithmes, on a besoin des algorithmes de base en arithmétique.
La plupart des problèmes posés en calcul formel nécessitent des calculs dont la taille croit de manière exponentielle voire doublement exponentielle en fonction de la taille des données et ce même si le résultat est lui aussi de taille petite. Un exemple est la réduction des systèmes de plusieurs équations polynomiales (bases de Groebner).
3.8.1 Algorithmes modulaires ou p-adiques
Dans certains cas, l’application de théories mathématiques parfois sophistiquées permet de réduire la complexité (par exemple, M. Van Hoeij a découvert récemment qu’un algorithme très utilisé en théorie des nombres, l’algorithme LLL, permettait d’améliorer la complexité d’une des étapes de la factorisation des polynomes à coefficients entiers sur les entiers). Heureusement, dans de nombreux cas, on peut réduire la complexité (donc le temps de calcul) par des adaptations au problème d’une même idée à condition de faire des hypothèses sur les données (autrement dit en abandonnant la volonté d’implémenter un algorithme très générique, ou tout au moins en spécialisant des algorithmes génériques). Par exemple lorsqu’on travaille avec des entiers (ou des polynômes à coefficients entiers, ou des matrices à coefficients entiers...) on utilise souvent des algorithmes modulaires et -adiques. Comme le calcul exact nécessite presque toujours de calculer avec des entiers, ces méthodes ont un rôle central en calcul formel, nous les présentons donc maintenant brièvement. Dans les prochaines sections, nous utiliserons ce type de méthode, par exemple pour le calcul de PGCD ou la factorisation de polynômes à coefficients entiers.
Les méthodes modulaires consistent à réduire un problème dans à son équivalent dans pour une ou plusieurs valeurs de , nombre premier. Le calcul dans a l’avantage de se faire avec des entiers dont la taille est bornée. Ensuite à l’aide d’estimations à priori sur la taille des solutions éventuelles du problème initial, on reconstruit la solution au problème initial avec le théorème des restes chinois.
Par exemple, on peut calculer un déterminant d’une matrice à coefficients entiers en cherchant ce déterminant dans pour plusieurs nombres premiers , dont le produit est deux fois plus grand qu’une estimation à priori de la taille du déterminant (donnée par exemple par l’inégalité d’Hadamard, cf. Cohen, p. 50).
Les méthodes -adiques commencent de manière identique par un calcul dans , on augmente ensuite la précision de la solution en la «liftant»de vers ou vers (lift linéaire ou lift quadratique), on s’arrête lorsque est assez grand (à l’aide d’estimations à priori) et on reconstruit alors la solution initiale. L’étape de «lift»est en général un lemme de Hensel dont on verra quelques exemples dans les prochains articles. L’algorithme commun au lemme de Hensel et au théorème des restes chinois est l’identité de Bézout, que l’on retrouve d’ailleurs un peu partout (par exemple pour le calcul de primitives).
Illustrons cette méthode sur un exemple simple, la recherche de racines rationnelles d’un polynôme à coefficients entiers ou polynomiaux, avec et non nuls. L’algorithme générique (assez connu) consiste à chercher les diviseurs de et de et à tester toutes les fractions de ces diviseurs, on montre en effet aisément que si fraction irréductible est racine de alors divise et divise . Cet algorithme est très inefficace si ou est un grand entier (car on ne sait pas forcément le factoriser) ou s’il a beaucoup de facteurs premiers (la liste des diviseurs à tester est alors très grande).
Lorsque les coefficients de sont entiers, la recherche précédente revient à trouver un facteur à coefficients entiers de , on peut donc réduire le problème modulo un entier premier qui ne divise pas : si un tel facteur existe dans alors ce facteur (réduit modulo ) est un facteur de dans donc admet une racine dans (puisque est inversible modulo car on a choisi premier ne divisant pas ). On évalue maintenant en les éléments de . S’il n’y a pas de 0, alors n’a pas de racine rationnelle. S’il y a des racines, on va les lifter de dans .
On suppose donc que pour , il existe un entier tel que Il s’agit de trouver un entier tel que vérifie On applique la formule de Taylor à l’ordre 1 pour en , le reste est nul modulo , donc : soit finalement : On reconnaît au passage la méthode de Newton, pour qu’elle fonctionne il suffit que ce qui permet de l’inverser modulo (et c’est ici qu’intervient l’identité de Bézout). En pratique quand on factorise un polynôme, on commence par retirer les multiplicités, on peut donc supposer que est sans facteur multiple dans . Ceci n’entraîne pas forcément qu’il le reste dans ce qui crée une contrainte supplémentaire sur le choix de , à savoir que et restent premier entre eux dans (il existe forcément de tels , par exemple premier plus grand que le plus grand entier intervenant dans le calcul du PGCD de et dans ).
Reste donc à revenir dans à partir d’une racine dans (où on peut choisir ). On va maintenant utiliser la représentation modulaire symétrique : on prend comme représentant modulaire d’un entier dans l’unique entier congru à modulo qui est strictement compris entre et (si est pair, la deuxième inégalité est choisie large).
Si est un facteur de , alors est encore un facteur de (le quotient de par est à coefficients rationnels mais le facteur est à coefficients entiers). Si on a choisi tel que , l’écriture en représentation modulaire symétrique de est inchangée, en effet on a des estimations à priori sur les entiers et : et puisque divise et divise . Comme est égal à dans , il nous suffit d’écrire en représentation modulaire symétrique . Pour conclure, on sait que est un multiple entier de . On divise donc le facteur par le pgcd de et et on teste la divisibilité de par ce facteur réduit.
Exemple
Considérons le polynôme qui est sans facteur carré.
On ne peut pas choisir car on réduirait le degré, pour ,
on a qui est facteur de , pour , ,
on vérifie que et sont premiers entre eux (par exemple
avec GCDMOD
sur une HP49 où on aura fixé la variable MODULO
à 5).
On teste ensuite les entiers de -2 à 2 sur . Seul -1 est racine modulo 5 (), on va maintenant lifter .
L’estimation à priori est donc (), une itération suffira. On a , l’inverse de est -2 donc: et est racine de dans . On calcule ensuite en représentation symétrique, le PGCD de 2 et -3 est 1 donc on teste le facteur , ici il divise donc admet un unique facteur entier de degré 1 qui est .
3.8.2 Algorithmes déterministes. Algorithmes probabilistes: Las Vegas et Monte-Carlo
L’algorithme p-adique présenté ci-dessus est un algorithme déterministe, il renvoie toujours un résultat certifié et le temps de calcul nécessaire à son exécution ne dépend pas du hasard (sauf si on choisit le nombre premier au hasard...). Ce type d’algorithmes est parfois trop long par rapport à d’autres type d’algorithmes utilisant le hasard :
- les algorithmes de type Las Vegas. Ceux-ci utilisent un élément aléatoire (dont dépend le temps d’exécution) mais certifient le résultat. Par exemple pour calculer le polynôme caractéristique d’une matrice de taille , on choisit un vecteur aléatoirement et on cherche une relation linéaire entre , s’il n’y en a qu’une à constante multiplicative près, alors elle donne le polynôme caractéristique, sinon on se rabat sur une autre méthode (ou on renvoie une erreur).
- les algorithmes de type Monte-Carlo. Ceux-ci utilisent un élément aléatoire mais ne certifient pas le résultat, qui a une très faible probabilité d’être inexact. Par exemple, pour calculer un déterminant d’une matrice à coefficients entiers, on peut faire le calcul modulo plusieurs nombres premiers et reconstruire le résultat par le théorème des restes chinois et décider de s’arrêter lorsque le résultat reconstruit est stable pour un, deux, ... nombres premiers. L’inverse de la probabilité d’erreur est égale au produit des nombres premiers pour lesquel on observe la stabilité. Autre exemple: le test de pseudo-primalité de Miller-Rabin.
Dans Xcas, certains algorithmes sont de type Monte-Carlo par
défaut, notamment le calcul de déterminant de grandes matrices
à coefficients entiers ou de bases de Gröbner, et un warning
s’affiche alors. La variable
proba_epsilon
permet de régler le niveau de probabilité
d’erreur acceptée, on peut la mettre à 0 pour forcer l’utilisation
d’algorithmes déterministes ou de type Las Vegas avec certification
du résultat. Si l’on fait des calculs à but expérimental pour
établir une conjecture, il n’est pas nécessaire de certifier un
calcul et il ne sert à rien de mettre proba_epsilon
à 0.
Par contre, pour établir une preuve (au sens mathématique du terme) qui
nécessite un calcul fait sur machine,
on prendra soin de mettre proba_epsilon
à 0.
On remarquera au passage que ce type de
preuve ne peut se faire qu’avec un logiciel open-source, puisqu’il
faut aussi pouvoir montrer que l’algorithme utilisé
est correctement implémenté.
3.9 Entiers et polynômes.
L’ensemble des entiers relatifs est muni des opérations
+
et *
qui en font un anneau commutatif intègre.
Si et sont deux entiers naturels, , on définit le quotient euclidien de par comme le plus grand entier naturel tel que (l’ensemble des entiers naturels tels que est borné par donc admet un élément maximal), On définit le reste euclidien de par est alors . On vérifie que (sinon serait positif ou nul).
3.9.1 Petits et grands entiers
Les microprocesseurs sont capables d’effectuer des
opérations arithmétiques de base (toujours +,-
, pratiquement
toujours *
et la division euclidienne) sur des petits entiers compris dans l’intervalle
(entier signé) ou (entier non
signé), pour , , et (parfois aussi pour
). En cas de dépassement, les calculs sont faits modulo
.
Les logiciels de calcul doivent pouvoir travailler avec des entiers de
taille plus grande, il faut les stocker en mémoire et implémenter
les opérations arithmétiques de base en se ramenant à des petits
entiers.
Pour faire cela, on va écrire des entiers dans des bases adéquates.
Ecriture en base
On se fixe une fois pour toutes un entier . Soit , on peut alors écrire de manière unique sous la forme Les sont appelés bits si ou digits si (parfois aussi pour , par exemple ). Pour montrer l’unicité de l’écriture, on suppose qu’il y a deux écritures distinctes de ce type, et on regarde le coefficient d’indice maximal qui est différent (appelons le ), on a alors quitte à changer de signe, on peut supposer que le membre de gauche est strictement positif, on a alors Mais le membre de droite se majore par absurde.
L’existence se prouve par l’algorithme suivant qui utilise la division euclidienne.
# def ecriture_base(N,b): L=[] while N>0: L.append(N % b) N=N//b return L
onload
i.e. tant que , on calcule le reste de par , qui donne
le coefficient de poids le plus faible de l’écriture de en base
, on ajoute lécriture en base du quotient de par .
L’algorithme s’arrête au bout de partie entière de
itérations.
Réciproquement, on vérifie que l’écriture obtenue
convient en développant
On observe au passage que l’écriture de sous la forme ci-dessus
nécessite additions et multiplications, et est donc plus
efficace que le calcul sous la forme développée 1.
C’est la méthode dite de Hörner.
Exemple : en base . Pour écrire en base 2, on calcule le
reste de 12 par 2 donc , le quotient de 12 par 2 vaut 6, on
divise par 2, reste 0 donc , quotient 3, on divise par 2, reste
, quotient 1, on divise par 2, reste quotient 0 on
s’arrête, donc 12=0b1100
.
Réciproquement on a bien
Exercice : la commande b:=convert(N,base,b)
de Xcas effectue la conversion
d’un entier en la liste des coefficients de son écriture en base
, la réciproque étant convert(L,base,b)
ou
horner(L,b)
. Implémenter des fonctions équivalentes
dans votre langage de programmation préféré.
Exercice : comment passe-t-on simplement de la représentation d’un nombre en base 2 à un nombre en base 16 et réciproquement ?
Lien avec les polynômes de avec .
A tout entier naturel , on peut associer un polynôme à coefficients entiers qui est son écriture en base , les coefficients du polynôme sont dans l’intervalle . Réciproquement, si les coefficients d’un polynôme sont des entiers compris entre alors ils correspondent à l’écriture d’un entier en base .
On va voir que les opérations arithmétiques de base sur les grands entiers reviennent à effectuer des opérations de base sur les polynômes, avec une petite difficulté supplémentaire, il faut tenir compte de retenues. Les algorithmes naifs pour additionner et multiplier deux polynômes correspondent précisément aux algorithmes enseignés à l’école primaire pour effectuer l’addition ou la multiplication de deux entiers.
3.9.2 Opérations arithmétiques de base
Addition, soustraction
Si et , alors il faut donc additionner les digits de l’écriture en base comme pour additionner deux polynômes. On a , si , il n’y a rien à faire, sinon on remplace par et on ajoute 1 (retenue) à qui appartient à , etc. Le nombre d’opérations à effectuer est de l’ordre du maximum du nombre de bits/chiffres/digits de et .
Multiplication
Si et , alors on se ramène à la multiplication de deux polynômes : Si , par exemple ou , sera très souvent supérieur à , il y aura souvent des retenues! On peut regrouper les termes ayant le même exposant, en utilisant des décalages pour tenir compte de (c’est l’algorithme de multiplication posée en base 10) ou additionner au fur et à mesure au coefficient actuel de (exercice de la feuille de TD). En base 2, si on pose la multiplication comme en base 10, il est conseillé d’effectuer les additions une par une, sinon la prise en compte des retenues est délicate.
Le nombre de multiplications et d’additions est de l’ordre de (on peut montrer que c’est aussi le cas en tenant compte des retenues, car si une retenue se propage de positions dans la boucle intérieure en , elle ne pourra pas se propager de plus de 2 positions pour les itérations suivantes de la boucle intérieure).
Comme le résultat a pour taille ou bits/chiffres/digits, on peut espérer l’existence d’algorithmes plus efficaces. Nous allons en présenter un dans le cadre plus simple de la multiplication des polynômes (où on n’a pas à gérer les retenues).
Diviser pour régner: multiplication de Karatsuba.
Soient deux polynômes de degrés strictement inférieur à . On suppose que le cout d’une opération arithmétique dans l’ensemble des coefficients vaut 1 et on néglige les autres opérations (on suppose par exemple que l’ensemble des coefficients est fini). On écrit avec de degrés strictement inférieur à , on a alors : Il y a 4 produits de polynômes de degrés , mais au prix d’additions intermédiaires, on peut se ramener à 3 produits, en effet donc pour calculer le facteur de il suffit de soustraire à les produits et que l’on doit calculer par ailleurs. Au premier ordre, le temps nécessaire pour multiplier les 2 polynômes de degré est multiplié par 3, au lieu de 4.
Plus précisément, soit le temps nécessaire pour calculer le produit de 2
polynômes par cette méthode, on a alors
où représente le nombre d’additions ou de soustractions
pour former , , soustraire et , et tenir compte
du fait que les termes de degré de se combinent
avec ceux de degré de et les termes de degré
de avec ceux de degré de .
On en déduit
cette récurrence se résoud facilement par la commande
ce qui donne .
Asymptotiquement, ce qui est bien meilleur que la multiplication naive en , mais pour de petites valeurs de , la multiplication naive est plus rapide comme l’illustre le graphe ci-dessous (en rouge la multiplication naive, en bleu Karatsuba complet) :
plot([log(2*4^n),log(-8*2^n+9*3^n)], n=1..10,display=[red,blue])
onload
On utilise Karatsuba (récursivement) uniquement pour des valeurs de
suffisamment grandes (théoriquement lorsque , le surcout dû
aux additions est plus petit que la multiplication économisée,
soit soit , en pratique plutôt pour de l’ordre de quelques
dizaines selon les implémentations, car nous n’avons tenu compte
que des opérations arithmétiques).
Exercice : vérifier que la multiplication des entiers longs en langage Python se comporte pour grand comme la multiplication de Karatsuba.
Les logiciels de calcul intensif utilisent la plupart du temps des algorithmes plus efficaces lorsque est encore plus grand, qui se rapprochent de . C’est en particulier le cas de tous les logiciels de calcul formel qui utilisent la libraire GMP écrite en langage C (Xcas, Sagemath, Maple, Mathematica, ...). Ces algorithmes utilisent la transformation de Fourier rapide (FFT).
3.9.3 Opérations de base sur les petits entiers.
Les petits entiers sont représentés en base , les opérations de base sont directement faites par le microprocesseur en se ramenant aux calculs en base 2:
-
addition : 0+0=0=1+1 (avec retenue),
0+1=1=1+0 qui se traduisent enou exclusif
logique hors retenues. Les opérations bit à bit
se parallélisent facilement, conduisant à des temps
d’éxecution de 1 cycle.
01001111 + 01101011 ---------- 10111010
- multiplication : 0*0=0*1=1*0=0, 1*1=1 qui se traduit en et logique. Les opérations de multiplication se parallélisent aussi, conduisant à des temps d’exécution de 1 cycle sur la plupart des processeurs actuels (mais ce n’était pas vrai il y a une vingtaine d’années).
- l’algorithme de division euclidienne en base 2
peut se faire avec l’algorithme de la potence. La situation
est simplifiée parce que le bit du quotient à déterminer
ne peut être que 0 ou 1, il suffit de comparer avec le reste
en cours. Le temps d’exécution est significativement plus
élevé que celui d’une multiplication (plusieurs dizaines de cycle
si ou ), car n’est pas connu quand on calcule
, alors que et sont connus quand on calcule .
110000 | 101 -101 |--------- ---- | 1001 010 | 100 | 1000 | - 101 | ----- | 011 |
3.9.4 Opérations de base sur les grands entiers
Les grands entiers naturels sont représentés dans une base qui est une puissance de 2, , les coefficients de l’écriture étant de petits entiers, ils sont stockés en mémoire à des adresses consécutives. On utilise le plus souvent la taille des régistres du microprocesseur, parfois une valeur légèrement inférieure pour simplifier la gestion des retenues.
Les implémentations de la multiplication font intervenir des algorithmes plus efficaces (comme Karatsuba), ainsi que des découpages par blocs visant à minimiser les opérations de lecture/écriture en mémoire au profit de l’utilisation de régistres et des caches de bas niveaux. En effet, avec les processeurs modernes, le cout du calcul d’un produit de 2 grands entiers par la méthode naive est dominé par les accès mémoire et non par les opérations arithmétiques elles-même.
3.10 Euclide
Dans cette section, on se concentre sur tout ce qui tourne autour de la division euclidienne.
3.10.1 Division euclidienne.
On a vu la définition de la division euclidienne pour les entiers naturels . Pour les entiers relatifs, plusieurs conventions de signe sont possible pour et , par exemple on peut conserver la convention de signe de reste positif. On peut aussi adopter la convention du reste symétrique, c’est-á-dire que . En général, on peut supposer que , ce qui donne le programme suivant :
def rsym(a,b): r = a % b if r<0: r += b if r<=b//2: return r return r-b
onload
Optimisation : sur les microprocesseurs modernes, on évite si possible
l’utilisation de tests pour optimiser le temps de calcul. En C, si
, et de signe quelconque sont des entiers 31 bits,
unsigned(r)>>31
vaut 0 (si ) ou -1 (sinon) et on peut écrire :
int rsym(int r,int m){ r = r % m; r += (unsigned(r)>>31)*m; // make positive return r-(unsigned((m>>1)-r)>>31)*m; }
Pour les polynômes en une variable, si et sont deux polynômes à coefficients dans un corps , il existe un unique polynôme et un unique polynôme tels que : L’unicité vient du fait que est incompatible avec les degrés si . L’existence se prouve par récurrence sur la différence de degré entre et , si elle est négative , si elle est nulle est le quotient des coefficients dominants de de degré et de de degré , sinon, on pose , ce qui annule le coefficient dominant de et on est ramené à une différence de degré diminuée de 1 (au moins).
Remarque 1 : si et sont à coefficients entiers et si est unitaire, alors et sont à coefficients entiers. Sinon, on peut définir la pseudo-division de par en multipliant par le coefficient dominant de à la puissance la différence des degrés + 1.
Remarque 2 : pour les polynômes à plusieurs variables, il n’existe malheureusement pas de division euclidienne.
On dit que divise si le reste de la division de par est nul. Si et sont des entiers naturels non nuls et si alors . Si et sont des polynômes alors le degré de est inférieur ou égal au degré de .
3.10.2 Anneau euclidien
L’ensemble des entiers relatifs est un anneau commutatif, mais il a des propriétés supplémentaires :
- il n’y a pas de diviseurs de 0: si le produit de 2 entiers est nul, alors l’un deux l’est (on parle d’anneau intègre)
- il possède une opération de division euclidienne
Cette propriété est également vraie pour les polynômes à coefficients dans un corps.
Observation : les seuls entiers ayant un inverse pour la multiplication sont 1 et -1. Pour les polynômes à coefficients dans un corps , il s’agit des polynômes constants non nuls (ceci est une conséquence du degré d’un produit qui est égal à la somme des degrés des facteurs). Ces éléments, appelés inversibles de l’anneau, jouent un role important, en effet si divise , i.e. alors pour tout élément inversible , divise puisque . Dans le cas des entiers, cela signifie qu’on peut négliger le signe pour étudier la divisibilité, autrement dit on peut se limiter aux entiers naturels. Dans le cas des polynômes, on peut multiplier par n’importe quel coefficient non nul, autrement dit on peut se limiter aux polynômes unitaires dont le coefficient dominant est 1.
3.10.3 Le PGCD
Soient et deux entiers. On se restreint au cas des entiers naturels en enlevant les signes.
Si l’un des deux éléments est nul, disons , alors la réponse est . Sinon, comme 1 divise et de , l’ensemble des diviseurs communs à et est non vide, et son plus grand élément est inférieur ou égal au minimum de et de , le PGCD existe donc bien.
Pour le calculer, on observe que le PGCD de et de est le même que celui de et de , le reste de la division euclidienne de par . En effet si est un diviseur commun à et , alors et donc . Réciproquement si est un diviseur commun à et , alors et donc . Cet algorithme se traduit en un programme récursif :
def pgcdr(a,b): if b=0: return a return pgcdr(b,a % b)
onload
A chaque appel récursif, la valeur du reste diminue au moins de 1 puisque .
Au bout d’un nombre fini d’appels, et on a le PGCD.
Si on utilise le reste symétrique au lieu du reste euclidien
positif, le reste perd un bit dans son écriture en base 2 à chaque
itération, le nombre d’itérations est donc au plus égal au minimum
du nombre de bits de et .
Exercice : traduire l’algorithme en un programme non récursif.
Le même algorithme fonctionne pour les polynômes, cette fois on cherche un polynôme de degré le plus grand possible qui divise et . L’algorithme ci-dessus s’adapte en remplaçant la division euclidienne des entiers par celle des polynômes, et le nombre d’appels récursifs est fini car le degré du reste qui diminue de 1 au moins à chaque itération. On obtient alors un PGCD, les autres PGCD sont des multiples non nuls de ce PGCD.
Il existe une version adaptée à l’écriture en base 2 des entiers sur machine, appelée PGCD binaire, qui évite de devoir faire des divisions euclidiennes (les divisions par 2 sont des décalages)
- si ou est nul on renvoie l’autre,
- si et sont pairs pgcd(,)=2 pgcd(,), (ne peut se produire qu’au début)
- si est pair, impair, pgcd(,)=pgcd(,),
- le cas symétrique (ne peut se produire qu’au début)
- si les 2 sont impairs pgcd(,)=pgcd(,min(,))
Chaque itération nécessite au plus le nombre de bits de l’écriture en base 2 de et , et il y a au plus itérations (car on diminue de 1 le nombre de bits d’au moins un des 2 opérandes). Cet algorithme a donc un coût d’au plus .
3.10.4 L’identité de Bézout
On va construire deux entiers relatifs et tels que On considère la matrice qu’il faut interpréter ligne par ligne comme 1er coefficient 2ème coefficient=3ième coefficient. L’algorithme de construction de et est une version arithmétique de l’algorithme du pivot de Gauss, où on souhaite créer un zéro dans la dernière colonne de la matrice. Mais seules les manipulations de lignes du type avec entier sont admises. Le mieux que l’on puisse faire est donc de créer avec quotient euclidien de par . Il faudra donc plusieurs manipulations de lignes avant d’avoir un 0, et la dernière colonne sera la liste des restes successsifs de l’algorithme d’Euclide pour calculer le PGCD de et . La ligne précédent la ligne avec un 0 en dernière colonne se termine par le PGCD (dernier reste non nul) et les deux premières colonnes donnent les coefficients de Bézout. Exemple avec et
Algorithme :
- Initialiser deux listes et à et .
- Tant que (comme les indices
commencent à 0, est le dernier nombre de la liste ),
faire
- quotient euclidien de par ,
- prend la valeur ,
- Renvoyer
Exemple : exécutez la commande ci-dessous qui calcule
l’identité de Bézout pour 125 et 45 en affichant les étapes de
l’algorithme dans la partie basse de la page HTML
Explication : A chaque itération, l’élément l[2]
d’indice 2 de la liste (ou ) prend comme valeur l’un des restes
successifs de l’algorithme d’Euclide,
et on a un invariant de boucle a*l[0]+b*l[1]=l[2]
qui donne l’identité de Bézout lorsque l[2]
est le
dernier reste non nul.
Cet invariant se prouve facilement par récurrence.
Regardons maintenant la taille de et de . On peut supposer que quitte à échanger et et on ignore les cas triviaux où et/ou valent 1.
Preuve (abrégée) : On considére les trois suites (coefficients de la colonne d’indice 0), (colonne d’indice 1) et (colonne d’indice 2), ces suites vérifient la même relation de récurrence : où est le quotient de la division de par . On montre par récurrence au cran après Bézout, on a donc la suite est majorée par et On en déduit puisque .
Cet algorithme fonctionne à l’identique avec les polynômes à coefficients dans un corps, en utilisant la division euclidienne des polynômes.
L’identité de Bézout a de nombreuses conséquences en arithmétique.
3.10.5 Nombres premiers entre eux
Deux entiers sont dits premiers entre eux si leur PGCD vaut 1. Par exemple 22 et 15 sont premiers entre eux.
Si et sont premiers entre eux et si divise alors divise .
Preuve : par hypothèse, il existe un entier tel que et par Bézout, deux entiers tels que , on élimine entre ces deux équations ce qui donne er est bien multiple de .
En effet, il existe des entiers tels que Donc
Ces résultats restent vrais avec des polynômes à coefficients dans un corps (où premiers entre eux signifie que le PGCD est constant).
Application : décomposition d’une fraction
avec et premiers entre eux (entiers ou polynômes).
Il existe alors et tels que , donc et tels
que et
Exemple 1 : , on a
donc et
Exemple 2 : Les polynômes et sont premiers entre eux, on a l’identité de Bézout donc Ce qui permet de calculer une primitive de .
3.10.6 Les restes chinois
L’identité de Bézout a une application très importante en calcul formel, elle permet de reconstruire un entier dont on connait le reste de la division par des entiers premiers entre eux :
Soit et deux entiers naturels premiers entre eux, alors pour tout et entiers relatifs, il existe un entier tel que est divisible par et est divisible par . L’entier n’est pas unique, deux solutions et distinctes diffèrent par un multiple de .
Remarque : lorsqu’on reconstruit un entier naturel plus petit que , on choisit l’unique valeur de telle que (c’est le reste de la division par de n’importe quelle solution), lorsqu’on reconstruit un entier relatif de valeur absolue plus petite que , on choisit tel que (on prend le reste symétrique).
Preuve : Si on a deux solutions et alors est divisible par et qui sont premiers entre eux, donc est divisible par . Cherchons une solution, cela revient à chercher et tels que On a donc Or et sont premiers entre eux, donc par Bézout, il existe et tels que il suffit de multiplier par pour trouver un couple de valeurs qui convient.
On sait que le couple n’est pas unique, si est une autre solution, alors donc donc divise et divise avec le même quotient. Pour déterminer efficacement une valeur de qui convient, on calculera tel que par l’algorithme de Bézout, on multipliera par et on prendra pour le reste de par .
Exemple : trouver tel que le reste de par 13 est 11 et le reste de par 7 est 3. On veut donc On a l’identité de Bézout : on multiplie par -8 donc on peut prendre (), on préfère prendre le reste par 13 donc , (et ).
Remarque : en calcul formel, on prendra un produit de nombres premiers distincts, et un nombre premier distinct des précédents, on calcule la valeur de modulo chaque nombre premier et dès qu’on a assez de nombres premiers (i.e. si ) on en déduit . On parle d’algorithme modulaire. Par exemple, on peut calculer le déterminant d’une matrice à coefficients entiers de cette manière.
Cette méthode s’applique aussi aux polynômes, mais elle est moins utile, car l’interpolation polynomiale de Lagrange est plus efficace lorsque les nombres premiers sont remplacés par des
3.10.7 Nombres premiers, factorisation
Exemple : 17 est premier, mais pas 18. 1 n’est pas premier.
Un nombre non premier admet un diviseur différent de 1 et lui-même donc une factorisation avec et . On peut supposer que , pour savoir si un nombre est premier on peut donc tester que tous les entiers plus petits ou égaux à ne divisent pas .
En effet un diviseur commun de et est égal à et à par définition de et premiers, absurde.
On en déduit par récurrence :
On a vu que c’était le cas pour , pour , soit un diviseur commun de et , est donc égal à , donc divise . Comme , divise ce qui contredit l’hypothèse de récurrence.
Preuve de l’existence par récurrence. Pour , on a . Pour , si est premier, alors , sinon il admet un plus petit diviseur strictement supérieur à 1, on a avec premier et à qui on applique l’hypothèse de récurrence.
Exercice : traduire cette preuve en un algorithme.
Unicité: supposons . Donc divise . Comme est premier, il doit être égal à un des . On divise par ce facteur commun et on recommence.
Des résultats analogues s’appliquent pour les polynômes à coefficients dans un corps: on parle de facteur irréductible (analogue de nombre premier), divisible seulement par des polynomes constants et des polynomes multiples non nuls d’eux-mêmes. Par exemple tous les polynômes de degré 1 sont irréductibles. Un polynôme s’écrit de manière unique à permutation près et à inversibles près comme produit de facteurs irréductibles.
3.10.8 Remarque : la divisibilité est une relation d’ordre partiel
On observe que :
- Si divise et divise , alors donc les deux quotients et sont inverses l’un de l’autre, donc inversibles. Si on travaille sur les entiers naturels, , si on travaille sur les polynômes unitaires alors . Une relation vérifiant la propriété et entraine est appelée relation antisymétrique
- divise (quotient 1), on parle de relation réflexive
- Si divise et divise , alors divise . En effet et donc . On parle ici de relation transitive
Une relation réflexive, antisymétrique et transitive est appelée relation d’ordre. Ici l’ordre est partiel, on peut avoir des éléments qui ne sont pas comparables, par exemple 2 ne divise pas 3 et 3 ne divise pas 2. Lorsque deux éléments sont comparables, on parle de relation d’ordre total (par exemple sur les entiers ou les réels ou l’ordre lexicographique sur les chaines de caractères ou les listes).
On verra plus loin un autre type de relation, les relations d’équivalence, où on remplace l’antisymétrie par la symétrie ( entraine )
3.10.9 Prolongement : idéaux
Un idéal est un sous-ensemble d’un anneau qui est stable pour l’addition (la somme de deux éléments de l’idéal est encore dans l’idéal), et stable par multiplication par tout élément de l’anneau. Par exemple si on veut résoudre un système de plusieurs équations en une inconnue, l’ensemble des polynômes en une variable qui s’annulent en un point est un idéal.
Dans les anneaux euclidiens, tous les idéaux sont principaux, i.e. engendrés par un élément, on prend un plus petit entier en valeur absolue pour les entiers ou un plus petit polynôme en degré pour les polynômes, la division euclidienne par cet élément doit renvoyer 0 comme reste sinon on aurait un élément dans l’idéal plus petit. Dans l’exemple ci-dessus, on peut se ramener à une équation polynomiale en une variable (c’est un PGCD des polynômes des équations).
Dans les anneaux non euclidiens, comme par exemple les polynômes à plusieurs variables, les idéaux ne sont pas forcément engendrés par un seul élément, ce qui rend la résolution de systèmes polynomiaux à plusieurs inconnues beaucoup plus délicate qu’en une inconnue. Il est en général difficile de vérifier qu’un élément de l’anneau appartient ou non à l’idéal (alors que c’est juste une division dans le cas à une variable). Heureusement, il existe des familles particulières qui engendrent l’idéal pour lesquelles la vérification se fait par une généralisation de la division euclidienne. La recherche efficace de telles familles, appelées bases de Groebner, est un domaine de recherche actif!
3.11 Quelques algorithmes d’arithmétique de base.
- Les algorithmes de multiplication et division dit rapides des entiers et polynômes (Karatsuba, FFT, ...). Cf. par exemple Knuth. ou pour les entiers la documentation de GMP, ou infra pour Karatsuba.
- Au lieu de la division euclidienne, on utilise très souvent la pseudo-division pour les polynômes : étant donné deux polynômes et de degrés et à coefficients dans un anneau contenu dans un corps (par exemple ), on multiplie par une puissance du coefficient dominant de , plus précisément par , ce qui permet d’effectuer la division par sans que les coefficients sortent de l’anneau. On utilise cette méthode lorsqu’on peut multiplier les polynômes par des constantes sans changer le problème (par exemple pour l’algorithme d’Euclide).
- L’algorithme d’Euclide est un algorithme «générique»de calcul de PGCD. Il n’est en général pas utilisé tel quel. Pour les entiers on utilise une variation adaptée à la représentation binaire des entiers (cf. Cohen ou le manuel de GMP version 4 pour plus de détails).
- l’identité de Bézout, aussi appelée PGCD étendu. Étant donné
deux entiers ou deux polynômes et on calcule , et
tels que . On écrit la matrice :
où on remarque que pour chaque ligne le coefficient de la 1ère colonne
est égal à multiplié par le coefficient de la
2ème colonne additionné à multiplié par le coefficient de la
3ème colonne. Ce qui reste vrai si on effectue des
combinaisons linéaires de lignes (type réduction de Gauß).
Comme on travaille dans les entiers ou les polynômes, on remplace la
réduction de Gauß des matrices à coefficients réels par une combinaison
linéaire utilisant le quotient euclidien (entier ou polynomial
selon le cas)
de par . On obtient alors le reste en 1ère colonne :
et on recommence jusqu’à obtenir 0 en 1ère colonne.
L’avant-dernière ligne obtenue est l’identité de Bézout (la dernière
ligne donne les cofacteurs du PPCM de et ).
Si l’on veut l’inverse de modulo
on remarque qu’il n’est pas utile de calculer les coefficients
appartenant à la 3ème colonne. Enfin, les lignes intermédiaires
peuvent servir à reconstruire une fraction d’entier représentée
par un entier de lorsque le numérateur et le dénominateur
sont de valeur absolue inférieure à . Exemple :
Voir aussi dans Xcas la session exemple depuis le menu Exemple,arit,bezout.xws. - Le théorème des restes chinois. Si on connaît et avec et premiers entre eux, on détermine tel que . On a donc et on applique Bézout pour trouver ou , on en déduit . En pratique, on cherche un des coefficients de Bézout, par exemple on cherche tel que , on a alors : Si est petit devant (par exemple 32 bits), est aussi petit, on commence par réduire modulo , puis on multiplie par , on réduit à nouveau modulo et on multiple enfin par .
- L’algorithme de Hörner pour évaluer un polynôme en . Il consiste à réécrire sous la forme on calcule donc successivement ce qui nécessite multiplications et additions, donc une complexité (sur un corps fini ou un anneau dont les opérations se font en temps ) avec une constante optimale. Voir aussi l’exercice sur cette méthode et son application au calcul du développement de Taylor d’un polynôme dans la section 3.14. Voir aussi la session exemple depuis le menu Exemples, poly, horner.xws.
- La puissance rapide et les tests de pseudo-primalité. Il est essentiel d’avoir une méthode rapide permettant de générer des nombres premiers pour appliquer des méthodes modulaires et -adiques. On utilise souvent le test de Miller-Rabin, qui prolonge le petit théorème de Fermat (si est premier, alors ). Voir le manuel de programmation de Xcas.
3.11.1 Calcul de la racine carrée entière
Étant donné un entier , il s’agit de déterminer le plus grand entier tel que , est la racine carrée de . On choisit une base par exemple pour un humain ou une puissance de 2 pour une machine, et on écrit en base , en découpant les chiffres par blocs de 2 en commençant par la droite, par exemple 2 00 00 00. On initialise la racine carrée à 0 et son carré à 0, on va calculer la racine carrée entière bloc par bloc en commençant par la gauche. Pour calculer le bloc suivant, on multiplie par et par (c’est un simple décalage de l’écriture en ajoutant un ou deux zéros). Puis on ajoute les nombres impairs successifs , , ... à tant que l’on est inférieur à tronqué au bloc. Le nombre d’impairs successifs ajouté est ajouté à . En pratique, il suffit de conserver tronqué et de lui retrancher les impairs successifs.
Ainsi, pour 2 00 00 00, au 1er bloc 2, on initialise , on ajoute à qui vaut alors 1 et on s’arrête car 1+3 est supérieur à 2. On passe au 2ième bloc, tronqué vaut 100, vaut 10, vaut 21, on retranche donc à 100 successivement 21, 23, 25, 27 et on s’arrête car le reste est 4. Donc devient 14, et . On passe au troisième bloc, et donc , on retranche de 400 les impairs successifs à partir de 281, ce qui n’est possible qu’une seule fois, cela donne et . On passe au dernier bloc, et donc , on soustrait 2821, 2823, 2825, 2827 de 11900, il reste 604 et .
Exercice : calculer la quatrième décimale de de cette manière.
La complexité de cet algorithme est en . En effet, pour calculer un chiffre il faut faire un nombre de soustraction au plus égal à , ces soustractions ayant au plus le nombre de chiffres de en base . (On peut accélérer le calcul à la manière de Karatsuba en choisissant une base puissance de 2 (ou 10) de l’ordre de et en divisant pour régner).
isqrt(x):={ local l,j,k,s,n,N,res; l:=revlist(convert(x,base,100)); res:=seq(0,size(l)); s:=0; N:=0; pour k de 0 jusque size(l)-1 faire N := (N-s)*100+l[k]; n:=2*horner(res[0..k],10)+1; s:=n; // ajout de la somme des impairs consecutifs pour j de 0 jusque 10 faire si s>N alors break; fsi; n+=2; s+=n; fpour; s -= n; res[k]:=j; fpour; retourne horner(res,10); }:;
3.11.2 Bezout sur les entiers et les fractions continues
Il existe une variante de l’identité de Bézout présentée ci-dessus pour les entiers. Soient deux entiers, on pose où et est le reste de la division euclidienne de par ( le quotient), . Comme précedemment, chaque ligne s’obtient par combinaison linéaire des deux précédentes, mais cette fois avec une addition ce qui se traduit par : Les suites et sont alors strictement croissantes (à partir du rang 1 pour ). Au rang du dernier reste non nul on a : et au rang suivant : On montre par récurrence que et une relation analogue pour , on en déduit alors que et (ce sont les cofacteurs du PPCM de et ), en particulier les coefficients de Bézout vérifient et .
On va aussi voir que est la -ième réduite du développement en fractions continues de (donc les coefficients de Bézout se lisent sur l’avant-dernière réduite). On introduit la notation pour . On a alors : En effet : D’autre part, on montre par récurrence sur que si en effet au rang et pour l’induction : Donc au rang et pour , on obtient
Les fractions continues servent bien entendu aussi et d’abord à approcher les réels par des rationnels. L’algorithme de calcul des termes du développement est le suivant : Soit . On initialise et la liste des à vide. Puis on fait une boucle : on ajoute la partie entière de à la liste, on calcule la partie fractionnaire de , si elle est nulle on s’arrête (dans ce cas ), sinon on stocke dans l’inverse de cette partie fractionnaire et on recommence. On note classiquement : On a . Les suites et sont donc positives et strictement croissantes pour , puisque pour , , elles tendent vers l’infini au moins aussi vite que des suites de Fibonacci (à vitesse au moins géométrique donc). On a aussi aisément par récurrence : On montre aussi comme ci-dessus : On définit par , en faisant on a alors ce qui donne en fonction de et En faisant on obtient . On montre ensuite que les suites pour les indices pairs et impairs sont deux suites adjacentes qui convergent vers , et on a En effet, la dernière égalité est une conséquence immédiate de (4), la croissance ou décroissance des suites d’indice pair ou impair s’en déduit en ajoutant (5) au cran suivant. La convergence vient de la limite infinie de en l’infini. On a donc La convergence est d’autant plus rapide que les tendent rapidement vers l’infini, donc si les sont plus grands que 1. La convergence la plus lente correspond au cas où tous les cas du nombre d’or, ou à partir d’un certain rang (nombre de ).
3.11.3 La puissance rapide itérative
Pour calculer , on décompose en base 2
On initialise une variable B
à 1, B
vaudra en fin de calcul, on initialise une variable k
à .
On calcule dans une boucle les carrés successifs de que l’on stocke dans une variable A
(A
vaudra donc successivement ) et simultanément on teste si vaut 1 en prenant le reste de la
division par 2 de k
(dans ce cas on multuplie B
par
A
modulo ), on divise ensuite k
par 2 au sens du quotient
euclidien.
rapide(a,k,n):={ local A,B; A:=a; B:=1; tantque k!=0 faire si irem(k,2)==1 alors B:=irem(A*B,n); fsi; k:=iquo(k,2); A:=irem(A*A,n); ftantque; return B; }
Pour tester en mode pas à pas, ajoutez debug(
au
début de la commande qui précède
debug(rapide(123,4567890,123456))
3.11.4 Application de la puissance rapide: trouver un gńérateur de
Un entier est un générateur de si et seulement si son ordre est exactement . Si on suppose connue la factorisation de , cela revient à tester que pour tous les diviseurs premiers de . On essaie des entiers compris entre 1 et jusqu’à ce que ces tests soient vérifiés. La probabilité de réussite est de donc en moyenne le nombre de tests à effectuer est . On va montrer que cela se comporte au pire en .
Le cas le pire à taille de fixée est celui où on prend tous les facteurs premiers depuis 2 et où la multiplicité des dans la factorisation de est 1, dans ce cas On va observer ce qui se passe lorsque on prend de plus en plus de facteurs premiers. Fixons nous un entier assez grand, on prend d’abord tous les nombres premiers entre 2 et , puis on regarde comment évolue lorsqu’on ajoute les nombres premiers entre et , puis entre et , ... puis entre et . Avec le résultat sur la densité des nombres premiers, il va y avoir nombres premiers dans la première tranche, puis , ... puis , d’où la minoration Quand est grand, les log sont équivalents à et on se retrouve avec la série harmonique, donc un équivalent de la minoration de en . Comparons avec la taille de . On a , si on fait le même découpage en tranche, le log de est encadré à une constante multiplicative près par . Donc est en et au final on a une minoration de en donc un double log en la taille de .
3.11.5 Application de la puissance rapide: cryptographie RSA
Interprétation du codage et du décodage.
Nous revenons sur la cryptographie RSA, en utilisant les notions vue dans les sections précédentes. On rappelle qu’on se donne un entier produit de deux nombres premiers et . Les entiers et sont secrets alors que est public. On choisit ensuite deux entiers et tels que , ou encore et sont inverses modulo l’indicatrice d’Euler de : Le codage et le décodage se font avec l’algorithme de la puissance modulaire rapide : le cout est de et opérations.
On peut maintenant facilement montrer que ces deux fonctions sont réciproques l’une de l’autre lorsque est premier avec . En effet, on a donc :
Comment générer des clefs
On choisit et en utilisant le test de Miller-Rabin. Par
exemple
On choisit un couple de clefs privée-publique en utilisant
l’identité de Bézout (ou inverse modulaire). Par exemple
Ici, on a pris E
de sorte que l’exponentiation
modulaire rapide à la puissance E
nécessite peu d’opérations arithmétiques (17),
comparé au calcul de la puissance d
, ceci
permet de faire l’opération “publique” plus rapidement
(encodage ou vérification d’une signature) si le microprocesseur
a peu de ressources (par exemple puce d’une carte bancaire).
Sur quoi repose la sécurité de RSA.
-
Difficulté de factoriser .
Si on arrive à factoriser , tous les autres calculs se font en temps polynomial en donc la clef est compromise. Il faut bien choisir et pour que certains algorithmes de factorisation ne puissent pas s’appliquer. Par exemple choisir
n’est pas une bonne idée car a beaucoup trop de 0 dans son écriture décimale, on peut donc facilement le factoriser, en essayant les diviseurs de 801 ajoutés à . - Difficulté de trouver connaissant et , si on ne connait pas
la factorisation de .
On peut tenter de trouver sans chercher à factoriser . Notons que ce n’est pas avec l’identité de Bézout qu’on procèdera, car connaitre l’indicatrice d’Euler de permet de trouver et immédiatement. Une attaque de ce type pour et presque egaux et petit devant est décrite par exemple ici. - Difficulté de trouver connaissant . Il n’y a pas d’algorithme connu permettant de le faire efficacement dans tous les cas (c’est ce qu’on appelle le logarithme discret, cf. infra). Mais il faut quand même faire attention au choix de . Par exemple prendre pour minimiser le nombre d’étapes de la puissance modulaire n’est pas judicieux, car si un message commun est envoyé à 3 personnes ayant des clefs publiques , , , la connaissance de permet de retrouver par le lemme chinois si sont premiers entre eux 2 à deux, puis puis .
Logarithme discret
Il s’agit de résoudre en entier léquation Si est dans le sous-groupe multiplicatif engendré par , il y a des solutions qui sont égales modulo l’ordre de dans , et on peut noter la plus petite solution positive par analogie avec les réeks.
Pour trouver , on peut bien sur tester tous les entiers en partant de 1 (en multipliant par à chaque itération) jusqu’à trouver mais le cout est en .
Si est connu (par exemple si est premier) et si on connait la factorisation de on peut faire mieux, on se ramène d’abord à résoudre plusieurs équations , en prenant l’équation de départ à la puissance , (avec et ), puis on retrouve à partir des par les restes chinois appliqués à . Ces équations sont moins couteuses à résoudre, car si est un générateur de , l’ordre de est . On peut d’ailleurs appliquer une méthode -adique pour trouver et se ramener à des équations avec un d’ordre , en commençant par prendre l’équation à la puissance ce qui donne puis on cherche le 2ème terme du développement en puissance croissantes de en prenant l’équation à la puissance , etc.
On peut donc se ramener à résoudre une équation de logarithme discret avec d’ordre plus petit (premier ou suffisament petit pour pouvoir faire une recherche exhaustive) et dans le sous-groupe engendré par .
Lorsque la recherche exhaustive en (à des termes logarithmiques près) devient couteuse (par exemple de l’ordre de quelques milliers), il est possible d’utiliser une méthode déterministe (pas de bébé, pas de géant) ou probabiliste de type Pollard-rho pour diminuer le cout en (à des termes logarithmiques près).
La méthode pas de bébé, pas de géant décompose où est la partie entière d’un majorant de la racine carrée de l’ordre du sous-groupe engendré par . On calcule toutes les puissances -ième de de 0 à et on place les paires dans une liste triée selon la valeur de . On compare ensuite les pour q de 0 à avec les éléments de la table, si on a égalité on en déduit . La méthode de Pollard-rho permet de gagner sur le stockage de la table des pas de bébé.
On peut aussi rechercher des tels que se factorise sur une base de nombre premiers fixée à l’avance Ce qui permet de calculer les log en base des en résolvant un système linéaire dans (par restes chinois et calcul -adique).
On cherche alors une puissance telle que se factorise sur la base, ce qui permet de trouver le log de .
3.12 Algorithmes rapides sur les polynômes en une variable.
On a présenté à la section 3.9.2 un algorithme de multiplication rapide de polynômes. On verra à la section 25.2 un algorithme plus rapide. En utilisant un algorithme de multiplication rapide de polynômes, on peut accélérer la division euclidienne et l’algorithme d’Euclide.
3.12.1 Inverse modulo
Soit un polynôme de coefficient constant 1. On cherche de degré strictement plus petit que tel que . Pour , il suffit de poser . Supposons le problème résolu pour cherchons tel que Ceci équivaut à donc on calcule , on tronque pour avoir un polynôme de degré , on divise par (on sait que est divisible par ). On peut aussi calculer directement Pour passer de la précision à la précision , le coût est celui d’une multiplication de 2 polynômes en précision et d’une multiplication en précision , donc . Si est une puissance de 2, , le cout total est Si la multiplication est Karatsuba, on obtient la même complexité pour l’inverse. Si la multiplication est la FFT en , on obtient aussi la même complexité pour l’inverse.
3.12.2 Division euclidienne.
Soient et deux polynômes de degré respectifs et avec , on pose la division euclidienne : Le degré de est , le cout de l’algorithme naïf est donc proportionnel à . Lorsque et sont grands, on peut espérer trouver un algorithme plus efficace pour calculer et .
On peut ramener le calcul de à celui d’un inverse modulo une puissance de en faisant le changement de variables et de multiplier par . Si est le polynôme ayant les coefficients de écrits dans l’autre sens, on a Avec les mêmes conventions de notations pour , et (en considérant comme de degré exactement égal à ), on a : Donc , et ce qui détermine (car son degré est ).
On calcule ensuite et .
3.12.3 Euclide
L’observation de base pour accélérer Euclide est que pour faire une étape d’Euclide, on divise génériquement un polynôme de degré par un polynôme de degré , avec un quotient de degré 1, dont les deux coefficients sont entièrement déterminés par les deux coefficients dominants et sous-dominants de et .
On peut généraliser cette observation si on pense en terme de l’algorithme d’Euclide étendu (Bézout), le vecteur colonne des deux restes et successifs à l’étape s’obtient en faisant le produit d’une matrice 2,2 par le vecteur colonne et Les coefficients de ne dépendent que des quotients initiaux, ils sont donc de degré au plus (environ). Si on change les coefficients de plus bas degré de ou , les coefficients de degré plus grand que (environ) de et ne changent pas. On va prendre tout d’abord et (environ) pour et et plus loin dans l’algorithme . Les termes de de degré de et ne vont pas dépendre des termes de degré plus petit que de et , que l’on peut annuler, ce qui revient à mettre en facteur ou encore à effectuer le calcul des premières étapes avec uniquement les coefficients de plus haut degré de et . Comme on a fait étapes environ, on a diminué d’environ le degré de et , qui encadrent . On refait alors environ étapes en gardant les termes de degré plus grand que de et ( ci-dessus), on a alors diminué de le degré de la paire de restes successifs.
L’algorithme HGCD (half-GCD) calcule la matrice d’Euclide étendu qui permet de passer dans la suite des restes de avec deg>deg à avec degdeg. Si est impair, on pose , si est pair, .
- si par chance deg, on renvoie la matrice identité
- on garde les coefficients de et de degré au moins égal à , on appelle récursivement HGCD qui renvoie une matrice effectuant =environ étapes d’Euclide étendu,
- on calcule et en faisant le produit de matrice avec , on a degdeg. La valeur précise est l’arrondi supérieur de .
- si par chance, deg, on a fini, on renvoie
- on effectue une division euclidienne pour remplacer par qui sont alors de degré strictement plus petit que “”, ce qui correspond à une matrice élémentaire
- on calcule la matrice d’Euclide étendu pour et pour environ étapes par appel récursif de HGCD sur les environ coefficients de et de degré au moins “”. Précisément, si =deg, on prend pour valeur de “” l’entier .
- on renvoie le produit des 3 matrices
On a donc ramené hgcd en taille au calcul de deux hgcd en taille et des produits de taille au plus .
Pour une preuve rigoureuse, on renvoie à Yap.
3.13 Pour en savoir plus.
Sur des aspects plus théoriques :
- Knuth: TAOCP (The Art of Computer Programming), volumes 1 et suivants
- Henri Cohen: A Course in Computational Algebraic Number Theory
- Davenport, Siret, Tournier: Calcul formel: Systèmes et algorithmes de manipulations algébriques
Sur des aspects plus pratiques, quelques références en ligne, la plupart sont accessibles gratuitement :
-
le code source de Giac disponible à l’URL :
http://www-fourier.ujf-grenoble.fr/~parisse/giac.html
- en Maple, il est possible de
décompiler une instruction
Maple
avec la commande
eval(instruction);
après avoir tapé
interface(verboseproc=2);
- le source du plus ancien système de calcul formel
maxima
(devenu logiciel libre) pour les personnes familières du langage Lisp
http://sourceforge.net/projects/maxima
de même pour le système Axiom - le source de librairies plus spécialisées (GMP, MPFR, MPFI, GP-PARI, Singular, NTL, GAP, CoCoA, ...), rechercher ces mots-clefs sur un moteur de recherche.
3.14 Exercices sur types, calcul exact et approché, algorithmes de bases
Vous pouvez
tester directement dans votre navigateur
Pour télécharger et installer Xcas sur votre ordinateur, suivre
les instructions données sur
http://www-fourier.ujf-grenoble.fr/~parisse/giac_fr.html
Pour lancer xcas sous linux, cherchez Xcas dans le menu
Education ou ouvrir un fenêtre terminal et
taper la commande
xcas &
Lors de la première exécution, vous devrez choisir entre
différents types de syntaxe (compatible C, maple ou TI89). Vous
pouvez changer ce choix à tout moment en utilisant le menu
Configuration->mode (syntaxe). On vous propose aussi d’ouvrir le
tutoriel, qui est également accessible depuis le menu Aide,
Débuter en calcul formel.
L’aide en ligne est accessible en tapant ?nom_de_commande
.
Dans Xcas, vous pouvez aussi taper le début d’un
nom de commande puis la touche de tabulation (à gauche du A sur
un clavier francais), sélectionner la commande dans la boite
de dialogues puis cliquer sur Details pour avoir une aide plus
complète dans votre navigateur. Pour plus de détails sur
l’interface de Xcas, consultez le manuel (Aide->Interface).
Si vous n’avez jamais utilisé de logiciel de calcul formel,
vous pouvez commencer par lire le tutoriel (menu Aide->Debuter en
calcul formel->tutoriel) et faire certains des exercices
proposés (des corrigés sous forme de sessions Xcas sont
dans Aide->Debuter en calcul formel->solutions)
Il peut être interessant de tester ces exercices en parallèle avec Xcas et des calculatrices formelles....
-
À quelle vitesse votre logiciel multiplie-t-il des
grands entiers (en fonction du nombre de chiffres)?
On pourra tester le temps de calcul du produit
de où , , etc. .
Même question pour des polynômes en une variable (à générer
par exemple avec
symb2poly(randpoly(n))
ou avecpoly1[op(ranm(.))]
).
- Comparer le temps de calcul de par la fonction
powmod
et la méthode prendre le reste modulo après avoir calculé .
Programmez la méthode rapide et la méthode lente. Refaites la comparaison. Pour la méthode rapide, programmer aussi la version itérative utilisant la décomposition en base 2 de l’exposant : on stocke dans une variable locale les puissances successives , on forme en prenant le produit modulo de ces puissances successives lorsque le bit correspondant est à 1 (ce qui se détecte par le reste de divisions euclidiennes sucessives par 2, le calcul de et du bit correspondant se font dans une même boucle). - Déterminer un entier tel que ,
, et .
- Calculez dans
- Algorithmes fondementaux : écrire des programmes implémentant
- le pgcd de 2 entiers
- l’algorithme de Bézout
- l’inverse modulaire en ne calculant que ce qui est nécessaire dans l’algorithme de Bézout
- les restes chinois
- Construire un corps fini de cardinal 128 (
GF
), puis factoriser le polynôme où est un élément quelconque du corps fini. Comparer avec la valeur de .
- Utiliser la commande type ou whattype ou équivalent
pour déterminer la représentation
utilisée par le logiciel pour représenter
une fraction, un nombre complexe, un flottant en précision machine,
un flottant avec 100 décimales, la variable , l’expression ,
la fonction x->sin(x), une liste, une séquence, un vecteur,
une matrice. Essayez d’accéder aux parties de
l’objet pour les objets composites (en utilisant op par exemple).
- Comparer le type de l’objet
t
si on effectue la commandet[2]:=0;
après avoir purgét
ou après avoir affectét:=[1,2,3]
?
- Comparer l’effet de l’affectation dans une liste et dans un
vecteur ou une matrice sur votre logiciel (en Xcas, on peut utiliser
=<
au lieu de:=
pour stocker par référence).
- Le programme ci-dessous calcule la base utilisée
pour représenter les flottants.
Testez-le
et expliquez. - Déterminer le plus grand réel positif de la forme
( entier)
tel que renvoie 0 sur PC avec la précision par
défaut puis avec
Digits:=30
.
- Calculer la valeur de avec 30 chiffres
significatifs, puis sa partie fractionnaire. Proposez une commande
permettant de décider si est un entier.
- Déterminer la valeur et le signe de la fraction rationnelle
en et en faisant deux calculs, l’un en mode approché et
l’autre en mode exact. Que pensez-vous de ces résultats?
Combien de chiffres significatifs faut-il pour obtenir un résultat
raisonnable en mode approché?
- Que se passe-t-il si on essaie d’appliquer l’algorithme de la
puissance rapide pour calculer par exemple pour
?
Calculer le nombre
de termes dans le développement de et expliquez.
- Programmation de la méthode de Horner
Il s’agit d’évaluer efficacement un polynôme en un point. On pose et on écrit : où : On calcule alors par ordre décroissant , , ..., .- Donner en fonction de puis pour , en fonction de et . Indiquez le détail des calculs pour et une valeur de non nulle.
- Écrire un fonction
horn
effectuant ce calcul: on donnera en arguments le polynôme sous forme de la liste de ces coefficients (dans l’exemple[1,0,-2,5]
) et la valeur de et le programme renverra . (On pourra aussi renvoyer les coefficients de ). - En utilisant cette fonction, écrire une fonction qui calcule le développement de Taylor complet d’un polynôme en un point.
- 1
- Certains systèmes de calcul formel (calculatrices par exemple) utilisent d’ailleurs des méthodes spécifiques pour gérer le problème de la fragmentation de la mémoire, appelés “garbage collector”. Ce type de méthode est intégré dans des langages comme Lisp ou Java, en C/C++ on trouve des libraries pour cela, par exemple GC de Boehm, incluse dans la distribution de GCC.
- 2
- Les HP Prime utilisent Giac comme noyau de calcul formel, les TI Nspire CAS utilisent sans doute une version actualisée du système utilisé sur les TI 89, 92, Voayge 200.
- 3
- un quartet=un demi octet
- 4
- Plus précisément deux piles, la pile de donnée et la pile gérant le flux d’exécution. Cette dernière n’est pas visible par l’utilisateur
- 5
- Sauf si on utilise comme dernier argument le nombre d’arguments de la fonction ou si on utilise (cf. infra) un tag de début de liste d’arguments
- 6
- Toutefois une adaptation du logiciel utilisant comme quantum de base par exemple 32 bits porterait cette limite à
Chapitre 4 Les générateurs de nombres pseudo-aléatoires.
4.1 Selon la loi uniforme
Les générateurs d’entiers dans une plage donnée selon la loi uniforme servent en général de base pour générer des nombres aléatoires entiers ou non selon des lois classiques. Ils doivent à la fois être rapides, avoir une période égale à la plage donnée et avoir de bonnes propriétés statistiques.
Xcas utilise un “tiny” Mersenne Twister (de période environ ), certaines implantations de Giac utilisent un générateur congruentiel.
4.1.1 Les générateurs congruentiels à 1 cran.
Etant donnés trois entiers et on considère la suite où on choisit (par exemple) comme représentant de le reste de la division euclidienne par . La valeur de est appelée seed en anglais, elle est initialisée usuellement soit à 0 (ce qui permet de reproduire des bugs dans un programme dépendant du hasard), soit avec l’horloge système ou tout autre entrée de l’ordinateur (par exemple périphériques).
On supposera que , le cas n’est pas très intéressant. On a alors : On cherche à réaliser une période la plus grande possible idéalement , mais peut fort bien convenir, et c’est possible si est premier en choisissant générateur du groupe cyclique, car on a alors et : donc la suite est stationnaire ou prend toutes les valeurs sauf .
Exemple : choisir pour une puissance de 2 permet d’effectuer la division euclidienne très rapidement, mais cela a un inconvénient assez important : les bits de poids faible de ont une périodicité très (trop) petite. Il est alors intéressant de prendre , parce que la division euclidienne par peut se coder efficacement en base 2, on divise par (décalage de bits) et on ajuste . Ainsi pour et , est premier. On peut construire une suite de période 16 en choisissant générateur de , par exemple et donne la suite .
On a le :
- et sont premiers entre eux
- est divisible par tous les facteurs premiers de
- est multiple de 4 si l’est.
On observe d’abord que vouloir la périodicité maximale revient à pouvoir supposer que . Il est donc nécessaire d’avoir et premiers entre eux, sinon tous les sont multiples du pgcd de et . Ensuite, on pose la décomposition en facteurs premiers de et on raisonne modulo chaque premier (par le lemme chinois, la périodicité est le PPCM des périodicités modulo chaque ). Si alors est inversible modulo donc modulo on a et la valeur ne peut pas être atteinte (ou alors la suite est stationnaire). Donc doit être divisible par tous les facteurs premiers de pour avoir la périodicité maximale. Réciproquement, il faut trouver le premier ordre tel que . On pose , on a On sait que est un multiple de , disons , on en déduit que pour , on a bien , alors que pour et , . Le même calcul pour (prise en compte de la division par 2 de ) donne la condition est multiple de 4 si l’est.
On trouvera dans Knuth une discussion détaillée du choix de .
Exemple : est premier, on peut donc construire un
générateur congruentiel de période en choisissant
générateur de . Pour en trouver un, on peut tester
pris au hasard et voir si
pour tous les diviseurs premiers de . Par exemple
initialise l’état du générateur. Un appel à
r()
renvoie un entier entre 1 et , pour avoir
un g’enérateur pseudo-aléatoire selon la loi uniforme sur , on tape
.
Ainsi
permet de vérifier visuellement si les réels générés sont
bien répartis, ou bien
qui détecte des biais invisibles avec le test précédent, par
exemple pour
.
4.1.2 Récurrence à éléments
Au lieu d’une récurrence on conserve en mémoire valeurs successives de la suite et on calcule Si on note le vecteur et la matrice companion du polynôme , on a . Rechercher un générateur de période maximale revient à chercher d’ordre le plus grand possible, donc les valeurs propres de , i.e. les racines de , doivent être racines de l’unité d’ordre le plus grand possible donc . Ce que l’on peut faire en construire un polynôme irréductible primitif (cf. la section 16 sur la construction de représentation des corps finis).
4.1.3 Mersenne twister.
Ce sont des générateurs plus performants, avec un état interne en général plus grand, dont l’état initial est généré par un générateur congruentiel. Ils utilisent une relation de récurrence qui ressemble aux générateurs congruentiels, mais au lieu de travailler sur de grands entiers, on découpe l’entier en mots de taille gérée par le CPU, et on fait des opérations de type matriciels avec des opérations bit à bit (ou exclusif par exemple) au lieu d’opérations arithmétiques.
4.2 Selon plusieurs lois classiques
La méthode générale consiste à calculer la distribution cumulée de la loi et à prendre la fonction réciproque d’un réel généré aléatoirement entre 0 et 1 selon la loi uniforme. Lorsqu’on a un nombre discret de valeurs possibles pas trop grand et que l’on veut générer plusieurs nombres selon la même loi, on peut précalculer la distribution cumulée en chaque valeur, et faire une dichotomie pour trouver la valeur de la fonction réciproque du nombre aléatoire généré. Les calculs peuvent être rendus difficiles par des dépassement de capacité des flottants si on utilise des méthodes naives pour estimer les fonction de répartition. On trouvera dans Abramowitz-Stegun diverses formules pour initialiser les méthodes de Newton pour inverser les fonction de répartition courante.
Il existe aussi quelques cas particuliers où on peut obtenir plus facilement un réel selon la loi donnée :
-
Pour la loi normale, on génère 2 réels
entre 0 et 1, on calcule
En effet si on considère un couple de variables qui suivent une loi normale centrée réduite, la densité de probabilité au point (coordonnées cartésiennes) ou est : où . Donc suit une loi exponentielle (générée par la réciproque de la distribution cumulée) et uniforme, les deux sont indépendantes. On écrit alors . On peut pour le même prix générer .
Pour éviter de calculer des lignes trigonométriques, on peut aussi tirer et uniformément dans , accepter le tirage si et renvoyer deux valeurs aléatoires selon la loi normale - Pour la loi du à degrés de liberté, on fait la somme des carrés de réels aléatoires selon la loi normale
- Pour la loi de Student, on fait le quotient d’un réel selon la loi normale par la racine carrée d’un réel selon la loi du divisé par le nombre de degré de liberté
- Pour la loi de Fisher, on fait le quotient d’un réel selon la loi du en degrés de liberté divisé par et d’un réel selon la loi du en degrés de liberté divisé par
Chapitre 5 Le PGCD de polynômes.
Lorsqu’on travaille avec polynômes en une variable à coefficients sur un corps fini, l’algorithme d’Euclide est un algorithme efficace, pourvu que les degrés ne soient pas trop grands (dans ce cas il faut utiliser des algorithmes de type diviser pour régner, ici par exemple l’algorithme halfgcd qui consiste en gros à calculer d’abord l’identité de Bézout sous forme matricielle pour les polynômes tronqués en enlevant les degrés les plus bas). Mais l’algorithme d’Euclide est inefficace pour calculer le pgcd de deux polynômes à coefficients entiers ou à plusieurs variables. On va présenter ici les algorithmes utilisés habituellement par les systèmes de calcul formel: sous-résultant (PRS), modulaire (GCDMOD), -adique (EEZGD) et heuristique (GCDHEU). Le premier est une adaptation de l’algorithme d’Euclide et s’adapte à des coefficients assez génériques. Les trois autres ont en commun d’évaluer une ou plusieurs variables du polynôme (dans ce dernier cas il est nécessaire de bien distinguer le cas de polynômes à plusieurs variables) et de reconstruire le pgcd par des techniques distinctes, la plupart du temps ces algorithmes fonctionnent seulement si les coefficients sont entiers.
Soit donc et deux polynômes à coefficients dans un corps. Le pgcd de et n’est défini qu’à une constante près. Mais lorsque les coefficients de et sont dans un anneau euclidien comme par exemple ou , on appellera pgcd de et un polynôme tel que et soient encore à coefficients dans l’anneau, et que soit optimal, c’est-à-dire que si un multiple de vérifie et sont à coefficients dans l’anneau, alors est inversible.
La première étape d’un algorithme de calcul de pgcd consiste donc à diviser par son contenu (pgcd des coefficients entiers) chaque polynôme.
Exemple: et . Le polynôme est un pgcd de et puisqu’il est de degré maximal divisant et mais le pgcd de et est . Remarquons qu’avec notre définition convient aussi. Par convention on appelera pgcd dans le polynôme ayant un coefficient dominant positif.
Définition: On appelle contenu d’un polynôme le pgcd des coefficients de . On définit alors la partie primitive de : pp. Si , on dit que est primitif.
Proposition : Si et sont primitifs alors est
primitif.
Sinon, on prend un facteur premier du contenu de ,
donc ou modulo , absurde.
Proposition : le contenu de est le produit des contenus
de et de .
En effet le produit des contenus de et divise le contenu de ,
et /contenu de est primitif, /contenu de est primitif
donc le produit l’est,
Proposition : Si et sont primitifs et si divise dans alors .
Preuve : Soit . Soit le PPCM des dénominateurs des coefficients de et notons . On a donc donc le contenu de est le produit du contenu de par celui de , donc le contenu de est , donc .
Donc le PGCD de et , polynômes primitifs de est obtenu en prenant un PGCD de et dans , en multipliant par le PPCM des dénominateurs et en rendant le polynôme obtenu primitif (on change le signe du résultat si nécessaire pour avoir un coefficient dominant positif).
On en déduit que :
5.1 Le sous-résultant.
La première idée qui vient à l’esprit pour améliorer l’efficacité de l’algorithme d’Euclide consiste à éviter les fractions qui sont créées par les divisions euclidiennes. On utilise à cet effet la pseudo-division: au lieu de prendre le reste de la division euclidienne du polynôme par , on prend le reste de la division de par , où désigne le coefficient dominant de et la différence entre le degré de et de .
Exercice: En utilisant votre système de calcul formel préféré, calculez les restes intermédiaires générés dans l’algorithme d’Euclide lorsqu’on utilise la pseudo-division par exemple pour les polynômes et sa dérivée.
Une solution avec giac/xcas:
pgcd(a,b,prs):={ local P,p,Q,q,R,g,h,d,res; res:=NULL; // convertit a et b en polynomes listes // et extrait la partie primitive P:=symb2poly(a); p:=lgcd(P); // pgcd des elements de la liste P:=P/p; Q:=symb2poly(b); q:=lgcd(Q); Q:=Q/q; if (size(P)<size(Q)){ // echange P et Q R:=P; P:=Q; Q:=R; } // calcul du contenu du pgcd p:=gcd(p,q); g:=1; h:=1; while (size(Q)!=1){ q:=Q[0]; // coefficient dominant d:=size(P)-size(Q); R:=rem(q^(d+1)*P,Q); if (size(R)==0) return(p*poly12symb(Q/lgcd(Q),x)); P:=Q; Q:=R; if (prs==1) Q:=Q/content(Q); if (prs==2) Q:=R/(g*h^d); res:=res,Q; if (prs==2) g:=q; h:=q^d/h^(d-1); } return(p,res); }:;
On s’aperçoit que les coefficients croissent de manière exponentielle
(comparer avec ce qui se passe en mettant 1 comme dernier argument). La
deuxième idée qui vient naturellement est alors à chaque étape de rendre le
reste primitif, donc de diviser par le pgcd de ces coefficients. Cela
donne un algorithme plus efficace, mais encore assez peu efficace car à chaque
étape on doit calculer le pgcd de tous les coefficients, on peut imaginer le
temps que cela prendra en dimension 1 et à fortiori en dimension supérieure.
L’idéal serait de connaitre à l’avance une quantité suffisamment grande qui
divise tous les coefficients du reste.
C’est ici qu’intervient l’algorithme du sous-résultant : après chaque pseudo-division euclidienne, on exhibe un coefficient "magique" qui divise les coefficients du reste (pour tester mettre le dernier argument de pgcd à 2). Ce coefficient n’est pas le pgcd mais il est suffisamment grand pour qu’on évite la croissance exponentielle des coefficients.
Algorithme du sous-résultant
Arguments: 2 polynômes et primitifs. Valeur de retour: le pgcd de et .
Pour calculer le coefficient "magique" on utilise 2 variables auxiliaires et initialisées a 1.
Boucle à effectuer tant que est non nul:
- on note degre()-degre() et le coefficient dominant de
- on effectue la division euclidienne (sans fraction) de par , soit le reste
- Si est constant, on sort de l’algorithme en renvoyant 1 comme pgcd
- on recopie dans puis dans
- on recopie dans et dans .
Si on sort normalement de la boucle, est nul, on renvoie donc la partie primitive de qui est le pgcd cherché.
Pour tester l’algorithme avec xcas, il suffit de décommenter les deux lignes Q:=R/(g*h^d); et g:=q; h:=q^d/h (d-1); ci-dessus.
La preuve de l’algorithme est un peu longue et par ailleurs bien expliquée dans le 2ème tome de Knuth (The Art of Computer Programming, Semi-numerical Algorithms), on y renvoie donc le lecteur intéressé. L’idée générale (et l’origine du nom de l’algorithme) est de considérer la matrice de Sylvester des polynômes de départ et (celle dont le déterminant est appelé résultant de et ) et de traduire les pseudo-divisions qui permettent de calculer les restes successifs du sous-résultant en opération de ligne sur ces matrices. On démontre alors que les coefficients de divisés par peuvent être interprétés comme des déterminants de sous-matrices de la matrice de Sylvester après réduction et c’est cela qui permet de conclure qu’ils sont entiers.
Par exemple, supposons que , , ... diminuent de 1 en degré à chaque division (c’est le cas générique dans le déroulement de l’algorithme d’Euclide). Dans ce cas, , il s’agit par exemple de montrer que le reste de par est divisible par le carré du coefficient dominant de . Voyons comment on obtient les coefficients de à partir de la matrice de Sylvester de et . Prenons la sous-matrice constituée des 2 premières lignes de et des 3 premières lignes de et réduisons-la sous forme échelonnée sans introduire de dénominateur. On effectue et , ce qui correspond à l’élimination du terme en du quotient de par on effectue ensuite ce qui correspond à l’élimination du terme constant du quotient de par , on obtient si on enlève les lignes 3 et 4, et les colonnes 1 et 2, on obtient (après échanges de lignes) une sous-matrice de la matrice de Sylvester de et On recommence les opérations de réduction de cette sous-matrice correspondant à la division euclidienne de par , on obtient puis après suppression des colonnes 1 et 2 et des lignes 2 et 3 la ligne des coefficients de .
Supposons qu’on se limite dès le début de la réduction à ne garder que les colonnes 1 à 4 et une 5-ième colonne parmi les suivantes, on obtient à la fin de la réduction une matrice 1,1 qui contient un des coefficients de (selon le choix de la 5-ième colonne). Donc ce coefficient est égal au déterminant de la matrice 1,1 qui est égal, au signe près, au déterminant de la matrice 3,3 dont il est issu par notre réduction (en effet, dans la 2ième partie de la réduction, on a multiplié deux fois par , mais on doit ensuite diviser le déterminant par pour éliminer les colonnes 1 et 2). Quant au déterminant de la matrice 3,3, il se déduit du déterminant de la matrice 5,5 par multiplication par (2 lignes ont été multipliées 2 fois par ) et division par (élimination des colonnes 1 et 2). Au final, tout coefficient de est égal au produit d’un déterminant 5,5 extrait de la matrice de Sylvester de et par , qui est justement le coefficient “magique” par lequel on divise le reste de par lors de l’algorithme du sous-résultant.
5.2 Le pgcd en une variable.
5.2.1 Le pgcd heuristique.
On suppose ici que les coefficients sont entiers ou entiers de Gauss. On peut donc se ramener au cas où les polynômes sont primitifs.
L’idée consiste à évaluer et en un entier et à extraire des informations du pgcd des entiers et . Il faut donc un moyen de remonter de l’entier à un polynôme tel que . La méthode consiste à écrire en base l’entier , avec une particularité dans les divisions euclidiennes successives on utilise le reste symétrique (compris entre et ). Cette écriture donne les coefficients d’un polynôme unique. On extrait ensuite la partie primitive de ce polynôme . Lorsque est assez grand par rapport aux coefficients des polynômes et , si divise et , on va montrer que le pgcd de et de est .
On remarque tout d’abord que divise . En effet divise et donc pour tout entier (ou entier de Gauss) , divise et . Il existe donc une constante telle que On a aussi divise . Il existe donc un polynôme tel que : Nous devons prouver que est un polynôme constant. On suppose dans la suite que ce n’est pas le cas. Evaluons l’égalité précédente au point , on obtient Finalement La procédure de construction de nous donne une majoration de ces coefficients par , donc de par , donc divise un entier de module plus petit que , donc On considère maintenant les racines complexes du polynôme (il en existe au moins une puisqu’on a supposé non constant). On a: Donc, comme est un entier (ou entier de Gauss) non nul, sa norme est supérieure ou égale à 1 et : Il nous reste à majorer les racines de pour minorer . Comme divise il divise et donc les racines de sont des racines communes à et . On va appliquer le:
Application du lemme à : on a donc si on a choisi tel que , alors pour tout , donc qui contredit notre majoration de .
Pour finir la démonstration du théorème, il nous faut encore montrer le lemme. On a Donc Ici on peut supposer que , sinon le lemme est démontré, donc est positif et Remarques
- Le théorème publié par Char, Geddes et Gonnet porte sur des coefficients entiers et c’est comme cela qu’il est utilisé par les systèmes de calcul formel (en commençant historiquement par Maple). Peu de systèmes l’utilisent pour les polynômes à coefficients entiers de Gauss. On peut d’ailleurs généraliser le théorème à d’autres types de coefficients, à condition d’avoir un anneau euclidien plongé dans avec une minoration sur la valeur absolue des élements non nuls de l’anneau.
- Nous n’avons jusqu’à présent aucune certitude qu’il existe des entiers
tels que la partie primitive de divise et . Nous allons
montrer en utilisant l’identité de Bézout que pour assez grand c’est
toujours le cas. Plus précisément, on sait qu’il existe deux polynômes
et tels que
Attention toutefois, et sont à coefficients rationnels, pour avoir
des coefficients entiers, on doit multiplier par une constante entière
, donc en évaluant en on obtient l’existence d’une égalité à
coefficients entiers
Donc le pgcd de et divise . Comme est un multiple de , on en déduit que , où est un diviseur de . Si on a choisi tel que alors donc l’écriture symétrique en base de est . Donc la partie primitive de est , le pgcd de et .
Le contenu de est 6, celui de est 4.
On a donc pgcd des contenus = 2, . La valeur
initiale de est donc . On trouve . Le pgcd entier de 15 et 63 est 3 que nous écrivons symétriquement
en base 4 sous la forme , donc , sa partie
primitive est . On teste si divise et , c’est le cas,
donc c’est le pgcd de et et le pgcd de et est .
Algorithme gcdheu
En arguments deux polynômes et à coefficients entiers ou entiers
de Gauss. Retourne le pgcd de
et ou faux en cas d’échec.
- Calculer le contenu de et . Vérifier que les coefficients sont entiers de Gauss sinon retourner faux.
- Extraire la partie primitive de et de , calculer le pgcd des contenus de et
- Déterminer .
- Début de boucle: initialisation du nombre d’essais à 1, test d’arrêt sur un nombre maximal d’essais, avec changement de entre deux itérations (par exemple ).
- Calculer le pgcd de et puis son écriture symétrique en base dont on extrait la partie primitive .
- Si passer à l’itération suivante. De même pour .
- Retourner
- Fin de la boucle
- Retourner faux.
On remarque au passage qu’on a calculé le quotient de par et le quotient de par lorsque la procédure réussit. On peut donc passer à la procédure gcdheu deux paramètres supplémentaires par référence, les deux polynômes que l’on affectera en cas de succès, ce qui optimise la simplification d’une fraction de 2 polynômes.
5.2.2 Le pgcd modulaire
On part du fait que si est le pgcd de et dans (ou alors après réduction modulo un nombre premier qui ne divise pas les coefficients dominants de et , divise le pgcd de et dans (par convention, le pgcd dans est normalisé pour que son coefficient dominant vaille 1). Comme on calcule dans , les coefficients des restes intermédiaires de l’algorithme d’Euclide sont bornés, on évite ainsi la croissance exponentielle des coefficients. Il faudra ensuite reconstruire à partir de .
On remarque d’abord que si on trouve alors et sont premiers entre eux. En général, on peut seulement dire que le degré de est supérieur ou égal au degré de . En fait, le degré de est égal au degré de lorsque les restes de l’algorithme d’Euclide (calculé en effectuant des pseudo-divisions, cf. l’exercice 1) ont leur coefficient dominant non divisible par . Donc plus est grand, plus la probabilité est grande de trouver du bon degré.
Dans la suite, nous allons déterminer une borne à priori majorant les coefficients de . On utilisera ensuite la même méthode que dans l’algorithme modulaire de recherche de racines évidentes: on multiplie dans par le pgcd dans des coefficients dominants et de et . Soit le résultat écrit en représentation symétrique. Si et si est du bon degré, on montre de la même manière que . Comme on ne connait pas le degré de , on est obligé de tester si divise et . Si c’est le cas, alors divise donc puisque . Sinon, est un nombre premier malchanceux pour ce calcul de pgcd (), il faut essayer un autre premier.
Remarque: On serait tenté de dire que les coefficients de sont bornés par le plus grand coefficient de . C’est malheureusement faux, par exemple dont le plus grand coefficient est 2 divise dont le plus grand coefficient (en valeur absolue) est 1.
Soit un polynôme à coefficients entiers. On utilise la norme euclidienne On établit d’abord une majoration du produit des racines de norme supérieure à 1 de à l’aide de . Ensuite si est un diviseur de , le coefficient dominant de divise le coefficient dominant de et les racines de sont aussi des racines de . On pourra donc déterminer une majoration des polynômes symétriques des racines de et donc des coefficients de .
Pour prouver le lemme 17, on développe les produits de polynômes. On pose et on note la partie réelle. Les deux donnent bien le même résultat.
Soit la factorisation de sur . On introduit le polynôme qui d’après le lemme a la même norme que . La norme de majore donc le coefficient constant de d’où: On remarque que (7) reste vraie si on considère les racines de norme plus grande que 1 d’un diviseur de puisque le produit porte alors sur un sous-ensemble. On écrit maintenant l’expression des coefficients de à l’aide des racines de : Pour majorer , on commence par majorer par . On est donc ramené à majorer avec pour hypothèse une majoration de donnée par la relation (7). Pour cela, on cherche le maximum de sous les contraintes fixé et .
On va montrer que le maximum ne peut être atteint que si l’un des (et tous les autres . Sinon, quitte à réordonner supposons que les sont classés par ordre croissant. On a donc , on pose pour , et . Comparons et . Si le choix de parmi comporte et , le produit est inchangé. Sinon on a la somme de deux produits, l’un contenant et l’autre . On compare donc et avec . Comme puisque la différence est le produit de deux nombres positifs, on arrive à la contradiction souhaitée.
Ensuite on décompose les choix de en ceux contenant et des 1 et ceux ne contenant que des 1, d’où la majoration et finalement On peut en déduire une majoration indépendante de sur les coefficients de , en majorant par (puisque divise ) et les coefficients binomiaux par (obtenue en développant ). D’où le
Avec cette estimation, on en déduit que si est un premier plus grand que alors le pgcd trouvé dans va se reconstruire en un pgcd dans si son degré est le bon.
Malheureusement la borne précédente est souvent très grande par rapport aux coefficients du pgcd et calculer dans s’avèrera encore inefficace (surtout si le pgcd est 1). Cela reste vrai même si on optimise un peu la majoration (10) en repartant de (8).
L’idée est donc de travailler modulo plusieurs nombres premiers plus petits et reconstruire le pgcd des 2 polynômes à coefficients entiers à partir des pgcd des polynômes dans et du théorème des restes chinois. En pratique on prend des nombres premiers inférieurs à la racine carrée du plus grand entier hardware de la machine (donc plus petits que sur une machine 32 bits) ce qui permet d’utiliser l’arithmétique hardware du processeur sans risque de débordement.
Algorithme du PGCD modulaire en 1 variable:
En argument: 2 polynômes primitifs et à coefficients entiers. Le résultat renvoyé sera le polynôme pgcd.
Variable auxiliaire: un entier initialisé à 1 qui représente le produit des nombres premiers utilisés jusqu’ici et un polynôme initialisé à 0 qui représente le pgcd dans .
Boucle infinie :
- Chercher un nouveau nombre premier qui ne divise pas les coefficients dominants et de et
- Calculer le pgcd de et dans . Si =1, renvoyer 1.
- Si ou si le degré de est plus petit que le degré de , recopier dans et dans , passer à la 6ème étape
- Si le degré de est plus grand que celui de passer à l’itération suivante
- Si le degré de est égal au degré de , en utilisant le théorème des restes chinois, calculer un polynôme tel que modulo et modulo . Recopier dans et dans .
- Ecrire en représentation symétrique. Soit le résultat rendu primitif. Tester si divise et . Si c’est le cas, renvoyer , sinon passer à l’itération suivante.
Finalement on n’a pas utilisé , la borne de Landau-Mignotte. On peut penser que l’étape 6 ne devrait être effectuée que lorsque est plus grand que . En pratique, on effectue le test de l’étape 6 plus tôt parce que les coefficients du pgcd sont rarement aussi grand que . Mais pour éviter de faire le test trop tôt, on introduit une variable auxiliaire qui contient la valeur de de l’itération précédente et on ne fait le test que si (ou bien sûr si on a dépassé la borne).
Remarque:
L’algorithme ci-dessus fonctionne également pour des polynômes à plusieurs variables.
Exemple 1:
Calcul du pgcd de et . Prenons pour commencer . On trouve comme pgcd (en effet donc on cherchait le pgcd de et de ). On teste si divise et , ce n’est pas le cas donc on passe au nombre premier suivant. Pour , on trouve . Donc n’était pas un bon nombre premier pour ce calcul de pgcd puisqu’on a trouvé un pgcd de degré plus petit. On teste si divise et , c’est le cas ici donc on peut arrêter, le pgcd cherché est .
Exemple 2 :
Calcul du pgcd de et . Pour , on trouve un polynôme de degré 7. Pour , on trouve donc était une mauvaise réduction. Comme ne divise pas et , on passe à . On trouve . On applique le théorème des restes chinois qui va nous donner un polynôme dans . On cherche donc un entier congru à 2 modulo 5 et à 0 modulo 3, -3 est la solution (écrite en représentation symétrique), donc le polynôme modulo 15 est . Ce polynôme divise et , c’est donc le pgcd de et de .
5.3 Le pgcd à plusieurs variables.
5.3.1 Le pgcd heuristique.
On suppose comme dans le cas à une variable que les polynômes sont primitifs, donc qu’on a simplifié les polynômes par le pgcd entier de leurs coefficients entiers.
Le principe est identique à celui du PGCD à 1 variable, on évalue les deux polynômes et de variables en un et on calcule le pgcd des 2 polynômes et de variables. On remonte ensuite à un polynôme par écriture symétrique en base de et on teste si divise et . Il s’agit à nouveau de montrer que si est assez grand, alors est le pgcd cherché. On sait que divise . Il existe donc un polynôme de variables tel que . On sait aussi que divise , donc il existe un polynôme de variables tel que On évalue en et on obtient , où est un entier, donc Comme est un entier, et sont des polynômes constants. Comme précédemment, on a aussi puisque .
- Premier cas: si ne dépend que de la variable . On continue le raisonnement comme dans le cas unidimensionnel.
- Deuxième cas: si dépend d’une autre variable, par exemple . On regarde le coefficient de plus haut degre de par rapport a . Ce coefficient divise le coefficient de plus haut degre de et de par rapport a . Comme est constant, on en deduit que le coefficient de plus haut degre de et par rapport a est divisible par donc le coefficient de plus bas degre en de ces coefficients de plus haut degre est divisible par , ce qui contredit la majoration de ce coefficient.
En pratique, cet algorithme nécessite le calcul récursif de pgcd sans garantie de réussite. On l’évite donc s’il y a beaucoup de variables (la limite est par exemple de 5 pour MuPAD).
5.3.2 Le pgcd modulaire multivariables.
Ici, on travaille modulo , où désignent les variables des polynômes. On considère donc deux polynômes et comme polynômes de la variables avec des coefficients dans . On évalue en , on obtient deux polynômes en variables dont on calcule le pgcd (récursivement).
Il s’agit de reconstruire le pgcd par interpolation. Tout d’abord, on a une borne évidente sur le degré du pgcd par rapport à la variable , c’est le minimum des degrés par rapport à des polynômes et . A première vue, il suffit donc d’évaluer les polynômes en points .
Il faut toutefois prendre garde aux mauvaises évaluations et à la normalisation des pgcd avant d’interpoler. En effet, si désigne le pgcd de et et le pgcd de et de , on peut seulement dire divise . Plusieurs cas sont donc possibles lorsqu’on évalue en un nouveau point :
- l’un des degrés de est plus petit que le degré du polynôme reconstruit par interpolation jusque là. Dans ce cas, toutes les évaluations qui ont conduit à reconstruire étaient mauvaises. Il faut recommencer l’interpolation à zéro ou à partir de (si tous les degrés de sont inférieurs ou égaux aux degrés du reconstruit).
- l’un des degrés de est plus grand que le degré du reconstruit jusque là. Il faut alors ignorer .
- Tous les degrés de sont égaux aux degrés du reconstruit jusque là. Dans ce cas, est un multiple entier du polynôme reconstruit jusque là et évalué en . Si on suppose qu’on a pu s’arranger pour que ce multiple soit 1, on ajoute le point aux points d’évaluation précédents en posant:
On voit que les mauvaises évaluations se détectent simplement par les degrés. Pour la normalisation, on utilise une petite astuce: au lieu de reconstruire , on va reconstruire un multiple du pgcd (ce multiple appartiendra à . On voit maintenant et comme des polynômes en variables à coefficients dans . Alors lcoeff, le coefficient dominant de (relativement à l’ordre lexicographique sur les variables ), est un polynôme en qui divise le coefficient dominant de et de donc divise le coefficient dominant du pgcd des coefficients dominants de et de . On va donc reconstruire le polynôme : c’est-à-dire multiplié par un polynôme qui ne dépend que de .
Revenons à en un point de bonne évaluation. C’est un multiple entier de : Donc, comme polynômes de à coefficients dans ou dans , . Comme divise , il en est de même en donc lcoeff divise . On en déduit que qui est divisible par est divisible par . On va donc considérer le polynôme : ses coefficients sont entiers et son coefficient dominant est donc
Algorithme du pgcd modulaire à plusieurs variables (interpolation dense):
Arguments: 2 polynômes primitifs et de variables à coefficients entiers. Renvoie le pgcd de et .
- Si , renvoyer le pgcd de et en une variable.
- Test rapide de pgcd trivial par rapport à . On cherche des -uplets tels que et soient de même degré que et par rapport à la variable . On calcule le pgcd de ces 2 polynômes en une variable. Si le pgcd est constant, alors on retourne le pgcd des coefficients de et .
- On divise et par leur contenu respectifs vu comme polynômes en à coefficients dans , on note le pgcd des contenus. On calcule aussi le pgcd des coefficients dominants de et de .
- On initialise le pgcd reconstruit à 0, le polynôme d’interpolation à 1, la liste des degrés partiels du pgcd par rapport à au minimum des degrés partiels de et par rapport à , le nombre d’évaluation à 0 et l’ensemble des points d’interpolation à la liste vide.
- Boucle infinie:
- Faire =entier aléatoire n’appartenant pas à jusqu’à ce que
- Calculer le pgcd en variables de et .
- Si pour un indice au moins. Si , on pose , , , et , sinon on pose . On passe à l’itération suivante.
- Si , on passe à l’itération suivante.
- Si , on interpole:
- et ajouter à
- Si est strictement plus grand que le minimum des degrés partiels de et par rapport à , on pose la partie primitive de (vu comme polynôme à coefficients dans ), on teste si et sont divisibles par , si c’est le cas, on renvoie
On observe que dans cet algorithme, on fait le test de divisibilite de par et . En effet, même après avoir évalué en suffisamment de points, rien n’indique que tous ces points sont des points de bonne évaluation. En pratique cela reste extrêmement improbable. En pratique, on teste la divisibilité plus tôt, dès que n’est pas modifié par l’ajout d’un nouveau point à la liste des .
Il existe une variation de cet algorithme, appelé SPMOD (sparse modular), qui suppose que seuls les coefficients non nuls du pgcd en variables sont encore non nuls en variables (ce qui a de fortes chances d’être le cas). L’étape d’interpolation est alors remplacée par la résolution d’un sous-système d’un système de Vandermonde. Cette variation est intéressante si le nombre de coefficients non nuls en variables est petit devant le degré. Si elle échoue, on revient à l’interpolation dense.
Notons enfin qu’on peut appliquer cette méthode lorsque les coefficients de et sont dans mais il faut alors vérifier qu’on dispose de suffisamment de points d’interpolation. Ce qui en combinant avec l’algorithme modulaire à une variable donne un algorithme doublement modulaire pour calculer le pgcd de 2 polynômes à coefficients entiers. C’est cette méthode qu’utilise par exemple MuPAD (en essayant d’abord SPMOD puis l’interpolation dense).
Exemple:
Dans cet exemple, on donne et sous forme factorisée, le but étant de faire comprendre l’algorithme. En utilisation normale, on n’exécuterait cet algorithme que si et étaient développés.
.
Prenons comme variable et comme variable . Les coefficients dominants de et sont respectivement et donc .
En , n’est pas du bon degré.
En , et sont du bon degré. Leur pgcd est , , donc . On teste la divisibilité de par , le teste échoue.
En , et donc , . On interpole: On teste la divisibilité de par , le test échoue.
En , et donc , . On interpole: donc On divise par son contenu et on trouve qui est bien le pgcd de et .
5.3.3 EZGCD.
Il s’agit d’une méthode -adique. On évalue toutes les variables sauf une, on calcule le pgcd en une variable et on remonte au pgcd variable par variable (EEZGCD) ou toutes les variables simultanément (EZGCD) par un lemme de Hensel. Il semble qu’il est plus efficace de remonter les variables séparément.
Soit donc et deux polynômes primitifs dépendant des variables de pgcd , on fixe une des variables qu’on appelera dans la suite. Soient et les coefficients dominants de et par rapport à . On évalue et en un uplet tel que le degré de et par rapport à soit conservé après evaluation en . On suppose que a le même degré que . On a donc l’égalité: et de même en remplaçant par .
Pour pouvoir lifter cette égalité (c’est-à-dire généraliser à plusieurs variables), il faut que et soient premiers entre eux. Sinon, on peut essayer de lifter l’égalité analogue avec . En général, on montre qu’il existe un entier tel que et soient premiers entre eux. En effet, sinon au moins un des facteurs irréductibles de va diviser pour deux valeurs distinctes de et va donc diviser à la fois et en contradiction avec la définition de . On lifte alors l’égalité obtenue en remplaçant par ci-dessus. Dans la suite, on suppose qu’on peut prendre pour alléger les notations.
On va aussi supposer que . Sinon, on fait un changement d’origine sur les polynômes et pour que convienne, on calcule le pgcd et on lui applique la translation d’origine opposée.
On adopte ensuite la notation suivante: si est un entier, on dit qu’un polynôme est un si la valuation de vu comme polynôme en à coefficients dans est supérieure ou égale à , ou de manière équivalente si L’égalité à lifter se réécrit donc: où et sont premiers entre eux et de degré 0 par rapport aux variables . Cherchons et de degré 1 par rapport aux variables tels que Il faut donc résoudre On peut alors appliquer l’identité de Bézout qui permet de déterminer des polynômes et satisfaisant l’égalité ci-dessus (avec comme reste nul) puisque et sont premiers entre eux. De plus, on choisit et tels que et et . On tronque ensuite et en ne conservant que les termes de degré 1 par rapport à .
On trouve de la même manière par récurrence et homogènes de degré par rapport à , de degré par rapport à respectivement inférieur aux degrés de et de et tels que et .
Si on est bien en un point de bonne évaluation et si est plus grand que le degré total (par rapport aux variables ) du polynôme on va vérifier que . En effet, si on a deux suites de polynômes et et et satisfaisant (11) avec les même termes de degré zéro et , alors en prenant la différence, on obtient: On égale alors les termes homogènes de degré , pour , on obtient , donc divise qui est de degré strictement inférieur au degré de par rapport à (car on a l’inégalité large et les termes de plus haut degré sont égaux), donc et . On montre de la même manière que et . L’écriture est donc unique, c’est donc l’écriture en polynôme homogène de degré croissant de que l’on reconstruit.
Cet algorithme permet donc de reconstruire , il suffit de tester à chaque étape si divise . On appelle cette méthode de remontée lemme de Hensel linéaire. Il existe une variante dite lemme de Hensel quadratique qui consiste à passer de à . Elle nécessite toutefois un calcul supplémentaire, celui de l’identité de Bézout à près pour les polynômes et . Ce calcul se fait également par lifting.
Algorithme EZGCD (Hensel linéaire)
Arguments: 2 polynômes et à coefficients entiers et primitifs. Renvoie le pgcd de et ou false.
- Evaluer et en , vérifier que les coefficients dominants de et de ne s’annulent pas. Calculer le pgcd de et de . Prendre un autre point d’évaluation au hasard qui n’annule pas les coefficients dominants de et de et vérifier que le pgcd a le même degré que . Sinon, renvoyer false (on peut aussi faire une translation d’origine de et de en un autre point mais cela diminue l’efficacité de l’algorithme).
- On note et les coefficients dominants de et de par rapport à .
- Si et et divise renvoyer
- Si et et divise renvoyer
- Si ou si renvoyer false
- Boucle infinie sur entier initialisé à 0, incrémenté de 1 à chaque itération: si constant, alors arrêter la boucle
- Lifter l’égalité par remontée de Hensel linéaire ou quadratique. Si le résultat est false, renvoyer false. Sinon renvoyer le premier polynôme du résultat divisé par son contenu vu comme polynôme en à coefficients dans .
Arguments: un polynôme, =lcoeff son coefficient dominant, un facteur de ayant comme coefficient dominant et dont le cofacteur est premier avec .
Renvoie deux polynômes et tels que et et .
- Soit , , , , .
- Déterminer les deux polynômes et de l’identité de Bézout (tels que où est un entier).
- Boucle infinie avec un compteur initialisé à 1, incrémenté de 1 à
chaque itération
- Si , renvoyer false.
- Si divise , renvoyer et .
- Soit . Soit et , on a
- Remplacer par le reste de la division euclidienne de par et par le reste de la division euclidienne de par . La somme des deux quotients est égale au quotient euclidien de par , c’est-à-dire au coefficient dominant de divisé par le produit des coefficients dominants de et (qui sont égaux) donc on a l’égalité:
- Soit
et .
On ajoute à , ainsi et à , ainsi
Remarque: on montre alors que donc en utilisant les propriétés :
- Réduire et en éliminant les termes de degré strictement supérieur à par rapport à . S’il reste un coefficient non entier, renvoyer false
- Remplacer par et par , passer à l’itération suivante.
Exemple:
On a et , le pgcd est donc . On remarque que est premier avec le cofacteur de mais pas avec le cofacteur de . Si on évalue en un autre point, par exemple , on trouve un pgcd de même degré, donc 0 est vraissemblablement un bon point d’évaluation (ici on en est sûr puisque le pgcd de et se calcule à vue...). On a , on va donc lifter où et .
On calcule les polynômes de l’identité de Bézout et avec , puis à l’ordre : donc et .
Donc avec et . De
même, , avec donc . On remarque que et sont bien à près les facteurs de :
Une deuxième itération est nécessaire. On calcule
puis et . Ici les coefficients et sont nuls car n’a pas de partie homogène de degré 2. On trouve alors et . Pour calculer le pgcd, il suffit de calculer la
partie primitive de vu comme polynôme en , ici c’est encore car le
contenu de est 1 (remarque: pour le contenu est ).
On trouve donc comme pgcd.
5.4 Quel algorithme choisir?
Il est toujours judicieux de faire une évaluation en quelques uplets pour traquer les pgcd triviaux. (E)EZGCD sera efficace si (0,...,0) est un point de bonne évaluation et si le nombre de remontées nécessaires pour le lemme de Hensel est petit donc pour les pgcd de petit degré, GCDMOD est aussi efficace si le degré du pgcd est petit. Le sous-résultant est efficace pour les pgcd de grand degré car il y a alors peu de divisions euclidiennes à effectuer et les coefficients n’ont pas trop le temps de croitre. SPMOD est intéressant pour les polynômes creux de pgcd non trivial creux. GCDHEU est intéressant pour les problèmes relativement petits.
Avec des machines multiprocesseurs, on a probablement intérêt à lancer en parallèle plusieurs algorithmes et à s’arrêter dès que l’un deux recontre le succès.
5.5 Pour en savoir plus.
Parmi les références citées dans le premier article, ce sont les livres de Knuth, H. Cohen, et Davenport-Siret-Tournier qui traitent des algorithmes de pgcd. On peut bien sûr consulter le source de son système de calcul formel lorsqu’il est disponible :
-
pour MuPAD sur un système Unix, depuis le
répertoire d’installation de MuPAD (en général /usr/local/MuPAD)
après avoir désarchivé le fichier lib.tar du répertoire share/lib
par la commande
cd share/lib && tar xvf lib.tar
on trouve les algorithmes de calcul de PGCD dans le répertoire
share/lib/lib/POLYLIB/GCD - Pour l’algorithme EZGCD, je me suis inspiré de l’implémentation de Singular (logiciel libre disponible à www.singular.uni-kl.de)
Sur le web on trouve quelques articles en lignes sur le
sujet en cherchant les mots clefs GCDHEU, EZGCD, SPMOD sur un moteur de
recherche, il y a par exemple une description un peu différente du pgcd
heuristique sur:
www.inf.ethz.ch/personal/gonnet/CAII/HeuristicAlgorithms/node1.html
et un article de comparaison de ces algorithmes
par Fateman et Liao (dont la référence bibliographique est
Evaluation of the heuristic polynomial GCD.
in: ISSAC pages 240–247, 1995). Quelques autres références :
- K.O.Geddes et al "Alg. for Computer Algebra", Kluwer 1992.
- pour GCDHEU Char, Geddes, Gonnet, Gcdheu: Heuristic polynomial gcd algorithm based on integer gcd computation, in: Journal of Symbolic Computation, 7:31–48, 1989.
- pour SPMOD "Probabilistic Algorithms for Sparse Polynomials", in: Symbolic & Algebraic Comp. (Ed E.W.Ng), Springer 1979, pp216,
Chapitre 6 Le résultant
6.1 Définition
Il s’agit d’un point de vue d’algèbre linéaire sur le PGCD. Considérons deux polynômes et à coefficients dans un corps, de degrés et et de pgcd et l’identité de Bézout correspondante : avec degré et degré. Imaginons qu’on cherche et en oubliant qu’il s’agit d’une identité de Bézout, en considérant simplement qu’il s’agit d’un problème d’algèbre linéaire de équations (obtenues en développant et en identifiant chaque puissance de de 0 à ) à inconnues (les coefficients de et les coefficients de ) On sait que et sont premiers entre eux si et seulement si ce problème d’algèbre linéaire a une solution pour . Donc si le déterminant du système est non nul, alors et sont premiers entre eux. Réciproquement si et sont premiers entre eux, le système a une solution unique non seulement avec comme second membre mais avec n’importe quel polynôme de degré inférieur , donc le déterminant du système est non nul.
Définition:
On appelle résultant de et le déterminant de ce système
(12). Il s’annule si et seulement si et
ne sont pas premiers entre eux (ont au moins une racine commune).
On appelle matrice de Sylvester la transposée de la matrice du système
(les inconnues étant par ordre décroissant les coefficients de
et )
(cette matrice contient degré lignes de coefficients
du polynôme et degré lignes de coefficients du
polynôme , les coefficients diagonaux sont les et )
Remarques
Le résultant s’exprime polynomialement en fonction
des coefficients des polynômes et donc aussi en fonction
des coefficients dominants de et et des racines
de et ,
or si on fait varier les racines de on annulera le résultant
si l’une d’elle coincide avec une racine de , ceci montre
que le résultant est divisible par le produit des
différences des racines de et .
On montre que le quotient
est en regardant le coefficient
dominant du résultant en degré total par rapport aux :
dans le déterminant il faut prendre le produit des termes diagonaux
pour avoir le degré maximal en les .
On peut aussi l’écrire sous la forme
Soit un polynôme de degré et coefficient dominant . Le coefficient dominant de est , un multiple de , le résultant de et est donc divisible par , on appelle le quotient discriminant. En terme des racines de , on a Ce résultat a un intérêt pour par exemple minorer à priori l’écart entre 2 racines d’un polynôme à coefficients entiers.
6.2 Applications
Revenons au cas où nos polynômes sont à coefficients dans un anneau contenu dans un corps, par e