Fractions - 2e partie

Pour multiplier deux fractions, comme (1/2) * (3/4), nous devons multiplier les numérateurs ensemble, faire la même chose pour les dénominateurs, pour obtenir une nouvelle fraction. Par  exemple :

1   3   3
- * - = -
2 4 8

Parfois, quand nous multiplions des fractions, le résultat peut être exprimé dans une forme plus simple. Par exemple :

2    11   22   1
-- * -- = -- = -
11 6 66 3

Dans cette leçon, nous allons enseigner à Python comment multiplier deux fractions, et aussi comment simplifier le résultat.


Multiplier deux fractions

Avec les deux fractions définies dans la dernière section, essayez de faire ce qui suit :

    print a*b

Essayez ! Vous obtiendrez un message d'erreur, comme Python ne sait pas ce que cela veut dire d'avoir le symbole "*" entre deux objets de la classe Fraction. Pour que Python comprenne qu'utiliser le symbole "*" entre deux fractions signifie que nous voulons multiplier ces deux fractions, nous utilisons la fonction privée __mul__(). Voici notre nouveau code :

  1 class Fraction(object):
2 def __init__(self, numerateur, denominateur=1):
3 self.num = numerateur
4 self.denom = denominateur
5
6 def __str__(self):
7 if self.denom == 1:
8 return "(%s)"%self.num
9 return "(%s/%s)"%(self.num, self.denom)
10 11 def __mul__(self, autre):
12 num = self.num * autre.num
13 denom = self.denom * autre.denom
14 return Fraction(num, denom)
15 16 #== zone de test ci-dessous ===
17 18 if __name__ == "__main__":
19 a = Fraction(1, 2)
20 b = Fraction(3, 1)
21 assert str(a) == "(1/2)" 22 assert str(b) == "(3)" 23 print a*b

D'abord, j'ai fait quelques changements mineurs dans les lignes 7, 8 et 22, pour que le dénominateur n'apparaisse pas quand il est égal à 1. C'est légèrement différent de l'exercice I suggéré dans la dernière leçon, puisque j'ai choisi de garder les parenthèses pour indiquer que c'était toujours un objet de la classe Fraction et non simplement un entier.

Ensuite, j'ai dit à Python que, pour multiplier deux fractions, on multiplie les numérateurs ensemble [ligne 12], on fait la même chose avec les dénominateurs [ligne 13] et que le résultat est un nouvel objet Fraction [ligne 14]. Si vous essayez d'exécuter ce programme, le résultat [donné par la ligne 23] sera "(3/2)", qui est ce que nous voulions.

Maintenant, transformons l'instruction print qui fonctionne en une instruction assert [ligne 23], et ajoutons un nouveau test [lignes 24 et 25], comme indiqué ci-dessous.

 16 #== zone de test ci-dessous ===
17 18 if __name__ == "__main__":
19 a = Fraction(1, 2)
20 b = Fraction(3, 1)
21 assert str(a) == "(1/2)" 22 assert str(b) == "(3)" 23 assert str(a*b) == "(3/2)" 24 c = Fraction(1, 3)
25 print b*c

Si nous exécutons ce programme, nous obtiendrons la sortie "(3/3)" que nous reconnaissons comme étant la même chose que 1 ; ce serait bien si Python pouvait faire de même. Pour cela,  nous utiliserons un algorithme qui a été inventé il y a plus de 2000 ans !


Algorithme d'Euclide

En 300 avant J.-C., (c'est-à-dire 300 ans avant l'an 0), le philosophe grec Euclide a publié une série de 13 livres intitulés Éléments. Ces livres contenaient ce qui était connu à l'époque sur les mathématiques et la géométrie. Parmi d'autres chose, un algorithme pour trouver le plus grand commun diviseur (ou le plus grand facteur commun) y était inclus. Il est très probable que cet algorithme était connu avant la naissance d'Euclide ; certaines personnes pensent qu'il a été découvert il y a environ 2500 ans ! Guido van Rossum, le créateur de Python, a écrit une petite fonction qui implémente l'algorithme d'Euclide :

def gcd(a, b):
'''gcd retourne le plus grand commun diviseur (greatest common divisor)
de 2 entiers donnés.'''
while b:
a, b = b, a%b
return a

Je n'expliquerai pas l'algorithme d'Euclide ici. Cela vous sera laissé comme exercice. Cependant, je l'utiliserai pour définir une nouvelle fonction pour simplifier les résultats.

def simplifier(a, b):
'''divise deux entiers par leur facteur commun.''' facteur_commun = gcd(a, b) a /= facteur_commun b /= facteur_commun return a, b

Maintenant, voici la nouvelle définition de classe :

  1 def gcd(a, b):
2 '''gcd retourne le plus grand commun diviseur (greatest common divisor) 3 de 2 nombres donnés.''' 4 while b:
5 a, b = b, a%b
6 return a
7 8 class Fraction(object):
9 def __init__(self, numerateur, denominateur=1):
10 num, denom = self.simplifier(numerateur, denominateur)
11 self.num = num
12 self.denom = denom
13
14 def __str__(self):
15 if self.denom == 1:
16 return "(%s)"%self.num
17 return "(%s/%s)"%(self.num, self.denom)
18 19 def __mul__(self, autre):
20 num = self.num * autre.num
21 denom = self.denom * autre.denom
22 num, denom = self.simplifier(num, denom)
23 return Fraction(num, denom)
24 25 def simplifier(self, a, b):
26 '''divise deux entiers par leur facteur commun.''' 27 facteur_commun = gcd(a, b)
28 a /= facteur_commun
29 b /= facteur_commun
 30 return a, b
31 32 #== zone de test ci-dessous ===
33 34 if __name__ == "__main__":
35 a = Fraction(1, 2)
36 b = Fraction(3, 1)
37 assert str(a) == "(1/2)" 38 assert str(b) == "(3)" 39 assert str(a*b) == "(3/2)" 40 c = Fraction(1, 3)
41 assert str(b*c) == "(1)" 42 d = Fraction(5, 10)
43 assert str(d) == "(1/2)"

En lignes 1 à 6, nous avons introduit notre fonction gcd(). En la mettant en dehors de la définition de la classe, nous la rendons facilement disponible pour les autres programmeurs s'ils importent notre fichier. Simplement pour montrer l'autre alternative, nous avons mis la méthode simplifier() à l'intérieur de la définition de la classe, en lignes 25 à 30. Nous utilisons simplifier() en ligne 22 et la testons en lignes 39, 40 et 41.

Nous utilisons également simplifier() quand nous initialisons une nouvelle fraction, en ligne 10 [avec d'autres changements mineurs en lignes 11 et 12] pour que la fraction nouvellement créée soit toujours dans sa forme simplifiée. Cela est testé en lignes 42 et 43.

Regardez bien les changement que nous avons faits. Tout fonctionne correctement, mais il y a une ligne que nous avons ajoutée qui n'était pas requise puisqu'elle fait que Python essaie de calculer la même chose deux fois. Pouvez-vous voir laquelle ? Essayez de la trouver, avant de passer à la leçon suivante dans laquelle nous allons diviser des fractions.

précédent Fractions - 1re partie - début - Fractions - 3e partie suivant