simplifier racine carrée en Python

Simplifier une racine carrée en Python

  • Dernière modification de la publication :11 octobre 2023
  • Temps de lecture :11 min de lecture
  • Commentaires de la publication :0 commentaire

Loading

Simplifier une racine carrée en Python peut s’avérer un peu compliqué. C’est pourquoi j’ai créé une classe pour cela.

Simplifier une racine carrée en Python: introduction

J’aime beaucoup la POO et j’ai choisi, pour répondre à la problématique traitée dans cet article, ce paradigme de programmation.

J’ai donc souhaité écrire une classe Python permettant de représenter un radical, c’est-à-dire un nombre de la forme \(a\sqrt{b}\), où a et b sont deux nombres rationnels.

J’ai souhaité implémenter les opérations élémentaires (addition, multiplications) et l’affichage sous la forme la plus réduite possible.

Simplifier une racine carrée en Python: rappels mathématiques

Avant de se lancer dans un tel projet, aussi humble soit-il, il est peut-être nécessaire de faire quelques rappels concernant la simplification de racines carrées.

Prenons un exemple: on souhaite simplifier \(\sqrt{150}\). Pour cela, il faut décomposer en produit de facteurs premiers le radicande (ici, 150):$$150=2\times3\times5^2.$$

Ensuite, on doit extraire les facteurs qui sont élevés à un exposant pair:$$\sqrt{150}=5^{2/2}\sqrt{2\times3}=5\sqrt6.$$

À travers cet exemple, on comprend ce qu’il faut faire.

Vers l’implémentation Python

Pensons que le coefficient et le radicande sont des rationnels; par conséquent, je vais utiliser le module fractions.

Ensuite, il me faut implémenter une fonction qui décompose en produit de facteurs premiers le radicande. Je vais nommer cette fonction decomp(n). Je vais mettre cette fonction à l’extérieur de ma classe. Cette fonction retournera un dictionnaire de la forme { facteurs premiers : exposants }.

Il me faudra ensuite penser aux diverses méthodes de la classe.

Simplifier une racine carrée en Python: la classe

Le constructeur

class Rc:
    def __init__(self,a,b):
        self.coef = Fraction(a)
        self.radicand = Fraction(b)

Ici, rien de fou-fou… 🙂 On déclare les deux arguments comme des rationnels.

Une première méthode pour simplifier la racine carrée

    def simp(self):
        x = self.radicand
        numer = decomp(x.numerator)
        denom = decomp(x.denominator)
        coef_num, radicand_num = 1, 1
        for facteur, exposant in numer.items():
            coef_num *= facteur**(exposant//2)
            if exposant%2 == 1:
                radicand_num *= facteur
                
        coef_denom, radicand_denom = 1, 1
        for facteur, exposant in denom.items():
            coef_denom *= facteur**(exposant//2)
            if exposant%2 == 1:
                radicand_denom *= facteur
                
        return Fraction(coef_num,coef_denom)*self.coef, Fraction(radicand_num,radicand_denom)

Cette méthode, comme son nom peut le suggérer, consiste à simplifier une racine carrée sans coefficient (donc \(\sqrt{b}\)). La difficulté ici est que ce radicande est considéré comme une fraction. Il faut donc simplifier la racine carrée du numérateur et celle du dénominateur.

La méthode retourne un 2-uplet (coefficient, radicande).

La méthode __str__

Vous le savez déjà sûrement, mais lorsque vous tapez:

>>> print(bidule)

ce qui est affiché est ce que renvoie la méthode __str__ de l’objet bidule.

Ainsi, si je souhaite afficher lisiblement mon objet, je dois formater cet affichage.

    def __str__(self):
        c, r = self.simp()[0], self.simp()[1]
        if r == 1:
            return c.__str__()
        elif c == 1:
            return f'√({self.simp()[1]})'
        else:
            return f'({c})√({r})'

À ce stade de l’implémentation, on a:

>>> a = Rc(1,150)
>>> print( a )
(5)√(6)

L’apparence du résultat est un choix artistique personnel… Libre à vous de l’adapter à votre convenance. Il m’a juste paru plus clair de mettre les nombres entre parenthèses dans le cas où on manipulerait des fractions:

>>> a = Rc(1,Fraction(150,9072))
>>> print( a )
(5/6)√(1/42)

On peut alors bien lire:$$\sqrt{\frac{150}{9072}}=\frac{5}{6}\sqrt{\frac{1}{42}}.$$

Une alternative

Si ce formatage ne vous convient pas, ce que je peux aisément concevoir, et si vous préférez le formatage conventionnel \(k\sqrt{n}\), on peut utiliser la méthode légèrement modifiée:

    def __str__(self):
        c, r = self.simp()[0], self.simp()[1]
        
        if r.denominator != 1:
            c, r = Fraction(c.numerator,c.denominator*r.denominator), Fraction(r.denominator,1)
        
        if r == 1:
            return c.__str__()
        elif c == 1:
            return f'√({self.simp()[1]})'
        else:
            return f'({c})√({r})'
>>> a = Rc(1,Fraction(150,9072))
>>> print(a)
(5/252)√(42)

Il est vrai que l’on préfère écrire les résultats de sorte qu’il n’y ait pas de radical au dénominateur d’une fraction!

Méthode de multiplication

    def __mul__(self, other):
        c,r = other.coef, other.radicand
        return Rc( self.coef * c , self.radicand * r)
>>> a, b = Rc(1,150), Rc(1,9072)
>>> print( a*b )
(180)√(42)

Le produit est ici considéré comme une racine carrée, donc comme un objet de la même nature que ceux qui sont multipliés entre eux.

La méthode de soustraction

    def __sub__(self, other):
        return self.__add__(Rc(-other.coef,other.radicand))

Rien de plus simple quand on sait qu’une différence n’est qu’une somme dans le corps des nombres réels…

La méthode de division

    def __truediv__(self, other):
        return self.__mul__( Rc(1/other.coef,1/other.radicand) )

Rien de plus simple quand on sait qu’une division n’est qu’une multiplication dans le corps des nombres réels…

La méthode d’exponentiation

    def __pow__(self,n):
        r = Rc(1,1)
        for _ in range(n):
            r *= self
            
        return r

Rien de plus simple quand on sait que l’exponentiation est une suite de multiplications 🙂

>>> a = Rc(1,150)
>>> print( a**6 )
3375000
>>> print( a**7 )
(16875000)√(6)

L’implémentation complète

Allez, c’est cadeau mon coco, voici le tout:

from fractions import Fraction

def decomp(n):
    D = dict() # dictionnaire vide
    
    k = 2
    
    while n > 1:
        exposant = 0
        while n%k == 0:
            exposant = exposant + 1
            n = n/k
        if exposant != 0:
            D[k] = exposant
        k = k+1
        j = 2
        while k%j == 0:
            k = k + 1 
        
    return D

class Rc:
    def __init__(self,a,b):
        self.coef = Fraction(a)
        self.radicand = Fraction(b)
        
    def simp(self):
        x = self.radicand
        numer = decomp(x.numerator)
        denom = decomp(x.denominator)
        coef_num, radicand_num = 1, 1
        for facteur, exposant in numer.items():
            coef_num *= facteur**(exposant//2)
            if exposant%2 == 1:
                radicand_num *= facteur
                
        coef_denom, radicand_denom = 1, 1
        for facteur, exposant in denom.items():
            coef_denom *= facteur**(exposant//2)
            if exposant%2 == 1:
                radicand_denom *= facteur
                
        return Fraction(coef_num,coef_denom)*self.coef, Fraction(radicand_num,radicand_denom)
    
    def __str__(self):
        c, r = self.simp()[0], self.simp()[1]
        
        if r.denominator != 1:
            c, r = Fraction(c.numerator,c.denominator*r.denominator), Fraction(r.denominator,1)
        
        if r == 1:
            return c.__str__()
        elif c == 1:
            return f'√({self.simp()[1]})'
        else:
            return f'({c})√({r})'
        
    def __mul__(self, other):
        c,r = other.coef, other.radicand
        return Rc( self.coef * c , self.radicand * r)#.__str__()
        
    def __add__(self, other):
        c,r = other.coef, other.radicand
        s1, s2 = self.simp(), Rc(c,r).simp()
        if s1[1] == s2[1]:
            result = Rc(s1[0]+s2[0] , s1[1])
            return result.__str__()
        else:
            return self.__str__() + ' + ' + other.__str__()
        
    def __sub__(self, other):
        return self.__add__(Rc(-other.coef,other.radicand))
    
    def __truediv__(self, other):
        return self.__mul__( Rc(1/other.coef,1/other.radicand) )
    
    def __pow__(self,n):
        r = Rc(1,1)
        for _ in range(n):
            r *= self
            
        return r
5 1 vote
Évaluation de l'article
S’abonner
Notification pour
guest
0 Commentaires
Commentaires en ligne
Afficher tous les commentaires