Mapytex/documentation/source/presentation_calculs.rst

329 lines
11 KiB
ReStructuredText

Présentation des outils de calculs de Mapytex
=============================================
La partie calcul de Mapytex est un module python qui permet la manipulation d'expressions mathématiques.
Une de mes motivations principales pour développer cet outil est de pouvoir créer des devoirs aléatoirement tout en mettant à disposition des élèves une correction personnalisée. Générer des exercices aléatoirement me permet de me concentre sur la direction que je veux leur donner (par exemple un exercice avec une addition de fractions avec un dénominateur commun) et de laisser python ventiler valeurs en fonction de contraintes que j'aurai défini.
À la différence des logiciels de calcul formel classiques, ce module va être capable d'écrire les étapes qui mènent à la réponse.
Calculs et explications
-----------------------
Les calculs peuvent être déclarés à partir d'une chaine de caractères.
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.from_str("2 + 3*(4+5*6)")
>>> print(priorite)
2 + 3(4 + 5 * 6)
>>> ajout_fractions = Expression.from_str("2 / 5 + 2 / 3")
>>> print(ajout_fractions)
2 / 5 + 2 / 3
>>> doubledev = Expression.from_str("(x+2)(3x-4)")
>>> print(doubledev)
(x+2)(3x-4)
La simplification des expressions est basée sur la méthode **.simplify()**
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.from_str("2 + 3*(4+5*6)")
>>> resultat = priorite.simplify()
>>> print(resultat)
104
>>> ajout_fractions = Expression.from_str("2 / 5 + 2 / 3")
>>> resultat = ajout_fractions.simplify()
>>> print(resultat)
16 / 15
>>> doubledev = Expression.from_str("(x+2)(3x-4)")
>>> resultat = doubledev.simplify()
>>> print(resultat)
3x^2 - 8 + 2x
Pour avoir l'explication et le détail des calculs, il va falloir faire une boucle sur les étapes générées par la méthode **.explain()**
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.from_str("2 + 3*(4+5*6)")
>>> resultat = priorite.simplify()
>>> for s in resultat.explain():
... print(s)
2 + 3(4 + 5 * 6)
2 + 3(4 + 30)
2 + 3 * 34
2 + 102
104
>>> ajout_fractions = Expression.from_str("2 / 5 + 2 / 3")
>>> resultat = ajout_fractions.simplify()
>>> for s in resultat.explain():
... print(s)
2 / 5 + 2 / 3
\frac{2 * 3}{5 * 3} + \frac{2 * 5}{3 * 5}
6 / 15 + 10 / 15
\frac{6 + 10}{15}
16 / 15
>>> doubledev = Expression.from_str("(x+2)(3x-4)")
>>> resultat = doubledev.simplify()
>>> for s in resultat.explain():
... print(s)
(x + 2)(3x - 4)
x * 3x + x * - 4 + 2 * 3x + 2 * - 4
2 * 3 * x - 8 + 3x^2 - 4x
6x - 8 + 3x^2 - 4x
3x^2 + 6x - 4x - 8
3x^2 + (6 - 4) * x - 8
3x^2 - 8 + 2x
Types de données manipulables
-----------------------------
Génération aléatoire d'expressions
----------------------------------
Les expressions peuvent être générées aléatoirement avec la méthode **.random()**. Pour cela, on va utiliser un *template* ou modèle dans lequel, les valeurs à générées seront entre accolades {}.
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.random("{a} + {b} * 5")
>>> print(priorite)
2 + 3 * 5
>>> priorite = Expression.random("{a} + {b} * 5")
>>> print(priorite)
9 - 3 * 5
Les valeurs générées peuvent dépendre les unes des autres.
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.random("{a} + {2*a} * 5")
>>> print(priorite)
-1 - 2 * 5
>>> add_frac = Expression.random("{a} / {b} + {c} / {b*2}")
>>> print(add_frac)
\frac{- 2}{- 2} + \frac{2}{- 4}
Par défaut, les valeurs sont choisies aléatoirement entre -10 et 10 en évitant 0. On peut modifier ce comportement au niveau de toutes les variables en paramétrant *rejected* (valeurs à éviter) et *min_max* (bornes où choisir les valeurs). On peut aussi définir ces paramètres pour chaque variables avec *variables_scope*.
.. code-block:: python
>>> from mapytex import Expression
>>> priorite = Expression.random("{a} + {2*a} * 5", rejected=[1, 2], min_max=(0, 3))
>>> print(priorite)
0 + 0 * 5
>>> priorite = Expression.random("{a} + {b} * 5",
rejected=[1, 2], min_max=(0, 3),
variables_scope={'a':{'min_max':(2, 5)}}
)
>>> print(priorite)
5 + 3 * 5
Enfin, il est possible de définir des conditions sur les valeurs générées en paramétrant *conditions*.
.. code-block:: python
>>> from mapytex import Expression
>>> add_frac = Expression.random("{a} / {b} + {c} / {d}",
conditions = ['b%d==0', 'a!=b', 'c!=d']
)
>>> print(add_frac)
10 / 4 - 7 / 2
Par contre, rien n'est fait qui permettrait de savoir si un ensemble de conditions permet de générée les valeurs demandées. Comme la génération des variables se fait avec la méthode par rejet, si les conditions ne permettent pas générer des valeurs convenables, le programme moulinera dans le vide...
Il est prévu d'ajouter l'option *suffle* qui permettra de mélanger les termes des expressions.
Rendus
------
Par défaut, le rendu des expressions est en mode *texte*. Il est possible d'avoir aussi un rendu *latex*.
.. code-block:: python
>>> from mapytex import Expression
>>> e = Expression.from_str("2/3 + 4*5")
>>> print(e)
2 / 3 + 4 * 5
>>> Expression.set_render("tex")
>>> print(e)
\frac{2}{3} + 4 \times 5
>>> Expression.set_render("txt")
>>> print(e)
2 / 3 + 4 * 5
-----------------------------------
Ce module a pour but d'être un outil pour faciliter la construction
d'exercices et leurs correction. Il a pour but d'être le plus simple
possible d'utilisation afin que tout le monde avec un minimum de
connaissance en programmation puisse créer librement des exercices.
On remarque qu'un opération sur des expressions, ne fait pas de calculs.
Elle ne fait que "concaténer" les listes des tokens.
À l'inverse, les opérations sur les fractions ou les polynômes renvoient
la liste des étapes jusqu'à leur forme simplifiée
.. code-block:: python
>>> addition_fraction = fraction1 + fraction2
>>> print(addition_fraction)
7 / 6
>>> for i in addition_fraction.explain():
... print(i)
...
1 * 3 / 2 * 3 + 2 * 2 / 3 * 2
( 3 + 4 ) / 6
7 / 6
>>> r = p + q
>>> print(r)
4 x ^ 2 + 2 x + 1
>>> for i in r.explain():
... print(i)
...
3 x ^ 2 + x ^ 2 + 2 x + 1
( 3 + 1 ) x ^ 2 + 2 x + 1
4 x ^ 2 + 2 x + 1
Différents rendus
~~~~~~~~~~~~~~~~~
Comme dit dans l'introduction, il y a deux types de rendus: un rendu
texte (utilisé depuis le début) et un rendu latex.
Voici un exemple de l'utilisation du rendu latex (par défaut).
.. code-block:: python
>>> exp = Expression("1 + 2 / 5")
>>> for i in exp.simplify().explain():
... print(i)
...
1 + 2 / 5
( 1 * 5 ) / ( 1 * 5 ) + ( 2 * 1 ) / ( 5 * 1 )
( 5 + 2 ) / 5
7 / 5
Pour changer le rendu, on importe le rendu depuis *mapytex.render* et on
appelle la méthode de classe d'Expression *set_render*.
Voici un exemple d'utilisation du rendu txt
.. code-block:: python
>>> from mapytex import txt
>>> Expression.set_render(txt)
>>> exp = Expression("1 + 2 / 5")
>>> for i in exp.simplify().explain():
... print(i)
...
2 / 5 + 2 / 3
2 * 3 / 5 * 3 + 2 * 5 / 3 * 5
( 6 + 10 ) / 15
16 / 15
Générer des expressions aléatoirement.
--------------------------------------
Créer un expression
~~~~~~~~~~~~~~~~~~~
Pour créer une expression il faut au moins une chose: la forme de
l'expression. Toutes les lettres entre accolades seront remplacées par
des valeurs aléatoires (par défaut entre -10 et 10 et non nulles).
.. code-block:: python
>>> form = "2* {a} + 3"
>>> expression_aleatoire = Expression.random(form)
>>> print(expression_aleatoire)
'2 * 9 + 3'
>>> print(Expression.random(form,val_min = 30, val_max = 40))
'2 * 31 + 3'
Créer une expression avec conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Parfois il peut être nécessaire d'imposer des conditions sur les
éléments à générer pour créer des exercices spécifiques.
.. code-block:: python
>>> form = "{a} / {b} + {c} / {d}"
>>> conditions = ["abs({b}) != 1", "{d} > 1", "{b} != {d}", "gcd({a},{b}) == 1", "gcd({c},{d}) == 1"]
>>> addition_fraction_alea = Expression.random(form, conditions)
>>> print(addition_fraction_alea)
'4 / 5 + 9 / 7'
La méthode pour créer les valeurs avec des conditions est la méthode par
rejet. Elle n'est pas très efficace et rien n'est encore prévu dans le
cas où aucune valeur n'est possible.
Opérations avec les valeurs générées
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pour éviter de faire tourner la méthode par rejet trop longtemps, il est
possible de faire des calculs avec les valeurs générées.
.. code-block:: python
>>> form = "{a} / {b} + {c} / {k*b}"
>>> conditions = ["abs({b}) != 1", "{k} > 1", "{b} != {d}", "gcd({a},{b}) == 1", "gcd({c},{k*b}) == 1"]
>>> random_frac_add_generator = RdExpression(form, conditions)
>>> print(random_frac_add_generator())
-9 / ( 7 ) + 1 / 28
Aléatoire sans rendu
~~~~~~~~~~~~~~~~~~~~~
On peut vouloir ne pas passer par la classe Expression pour obtenir
notre expression (si l'on veut utiliser la racine carré par exemple, ou
pour créer n'importe quoi qui ne fait pas intervenir d'expression).
Ainsi mapytex ne gère plus le rendu de l'expression ni son calcul.
La fonction qui permet de faire cela est *random_str*:
.. code-block:: python
>>> from mapytex import random_str
>>> form = "{a} / {b} + {c} / {k*b}"
>>> conditions = ["abs({b}) != 1", "{d} > 1", "{b} != {d}", "gcd({a},{b}) == 1", "gcd({c},{k*b}) == 1"]
>>> str_addition_fraction = random_str(form, conditions)
>>> type(str_addition_fraction)
str
>>> print(str_addition_fraction)
-2 / 5 + -8 / 35
>>> form = "A({a},{b}), B({2*a}, {3*b})"
>>> points_alea = random_str(form)
>>> points_alea
'A(7,5), B(14, 15)'
On remarque le défaut d'utiliser cette forme, le rendu est moins bien
fait (dans l'exemple, il n'y a pas de parenthèses autour du -8).