Tapez Python pour implémenter l'expansion algébrique (3) ~ Anneau polygonal, anneau résiduel du polynôme, expansion du champ ~

C'est la finale de la 3 série.

Jusqu'à la dernière fois, j'ai créé diverses classes dans la gamme des entiers et des nombres rationnels.

Cette fois, nous allons d'abord créer une classe qui gère les polynomies d'ordre $ n $, puis gérer les polynomies avec la classe d'anneau de classe restante que nous avons créée la dernière fois. Et enfin, nous élargirons le champ des nombres rationnels.

table des matières

  1. Monoid / Group / Ring / Integer Ring
  2. Champ, champ de nombres rationnels, anneau euclidien, anneau de reste, corps fini
  3. Anneau polygonal, anneau restant de polypoly, expansion du champ

la mise en oeuvre

Anneau de polygone

Afin d'effectuer une expansion algébrique, il est nécessaire de traiter des polynômes. Par conséquent, nous définissons ici une classe qui gère les polynomies $ n $ degrés (** anneau polymorphe **) et faisons les derniers préparatifs pour l'expansion algébrique.

Le polypole d'ordre $ n $ se compose de coefficients $ n + 1 $ et de la variable $ x $, y compris le terme constant.

\sum_{k=0}^n a_k x^k

Par conséquent, dans l'anneau polymorphe, le coefficient $ a_k $ se présente sous la forme d'une liste.

On peut voir que l'addition, la multiplication et la soustraction (éléments inverses supplémentaires) peuvent toujours être calculées pour les polynomies, mais elles ne sont pas toujours divisibles lorsqu'elles sont divisées par des polynomies, ce qui entraîne un ** anneau **. De plus, puisque vous pouvez définir la division de la troncature et le reste, vous pouvez voir qu'il devient le ** anneau euclidien ** qui est apparu la dernière fois.

La classe Polynomial définie ici hérite de ʻEuclidianRing et en même temps hérite de Generic [PolynomialBaseType]`. Si vous en héritez, «Polynomial» sera traité comme une classe générique, et si vous l'écrivez sous la forme de «Polynomial [T]» dans le code, le type décrit dans la partie de «T» sera affecté à «PolynomialBaseType». Est vérifié (similaire à un modèle C ++).

from typing import Generic, List
from itertools import dropwhile, zip_longest

PolynomialBaseType = TypeVar("PolynomialBaseType", bound=Field)

class Polynomial(EuclidianRing, Generic[PolynomialBaseType]):
    def __init__(self: "Polynomial[PolynomialBaseType]", *args: PolynomialBaseType) -> None:
        self.coeffs = list(dropwhile(lambda x: x == x.zero(), args))
        if not self.coeffs:
            self.coeffs = [args[0].zero()]

    @staticmethod
    def _mul_poly(a: List[PolynomialBaseType], b: List[PolynomialBaseType]) -> List[PolynomialBaseType]:
        c = [a[0].zero() for i in range(len(a) + len(b) - 1)]
        for i, aa in enumerate(reversed(a)):
            for j, bb in enumerate(reversed(b)):
                c[i + j] += aa * bb
        return list(reversed(c))

    @staticmethod
    def _div_poly(a: List[PolynomialBaseType], b: List[PolynomialBaseType]) -> Tuple[List[PolynomialBaseType], List[PolynomialBaseType]]:
        q = [a[0].zero() for i in range(max(len(a) - len(b) + 1, 0))]
        r = a[:]
        for i in range(len(q)):
            q[i] = r[i] / b[0]
            for k in range(len(b)):
                r[i + k] -= b[k] * q[i]
        return ([a[0].zero()] + q, r)

    def __repr__(self: "Polynomial") -> str:
        s = " " + " + ".join("%s x ^ %d" % (repr(c), len(self.coeffs) - i - 1) for i, c in enumerate(self.coeffs) if c != c.zero()) + " "
        s = s.replace(" + -", " - ")
        s = s.replace(" x ^ 0 ", " ")
        s = s.replace(" x ^ 1 ", " x ")
        s = s.replace(" 1 x ", " x ")
        return s.strip()

    def __floordiv__(self: "Polynomial[PolynomialBaseType]", x: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        q, r = Polynomial._div_poly(self.coeffs, x.coeffs)
        return Polynomial[PolynomialBaseType](*q)

    def __mod__(self: "Polynomial[PolynomialBaseType]", x: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        q, r = Polynomial._div_poly(self.coeffs, x.coeffs)
        return Polynomial[PolynomialBaseType](*r)

    def __mul__(self: "Polynomial[PolynomialBaseType]", x: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        y = Polynomial._mul_poly(self.coeffs, x.coeffs)
        return Polynomial[PolynomialBaseType](*y)

    def __add__(self: "Polynomial[PolynomialBaseType]", x: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        y = list(a + b for a, b in zip_longest(reversed(self.coeffs), reversed(x.coeffs), fillvalue=self.coeffs[0].zero()))
        y.reverse()
        return Polynomial[PolynomialBaseType](*y)

    def __neg__(self: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        return Polynomial[PolynomialBaseType](*(-aforainself.coeffs))

    def __eq__(self, x):
        if not isinstance(x, Polynomial):
            return NotImplemented
        return self.coeffs == x.coeffs

    def identity(self: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        return Polynomial[PolynomialBaseType](self.coeffs[0].identity())

    def zero(self: "Polynomial[PolynomialBaseType]") -> "Polynomial[PolynomialBaseType]":
        return Polynomial[PolynomialBaseType](self.coeffs[0].zero())

    def euclid_gcd(self: "Polynomial[PolynomialBaseType]", b: "Polynomial[PolynomialBaseType]") -> Tuple["Polynomial[PolynomialBaseType]", "Polynomial[PolynomialBaseType]", "Polynomial[PolynomialBaseType]"]:
        p, q, r = super().euclid_gcd(b)
        r.coeffs = [x / r.coeffs[0] for x in r.coeffs]
        return (p, q, r)

Ici, la méthode euclidienne de division mutuelle «euclid_gcd» est remplacée et le processus de division de l'engagement maximum par le coefficient d'ordre le plus élevé est inclus. Cela permet d'obtenir des polynomies moniques lors du calcul des promesses maximales entre polynômes.

Le code est long, testons-le une fois.

f = Polynomial[Q](Q(Z(1)),Q(Z(1)))  # x + 1
g = Polynomial[Q](Q(Z(1)),-Q(Z(1))) # x - 1
print(f * g) # -> x ^ 2 - 1

x = Polynomial[Q](Q(Z(2)),Q(Z(1)),Q(Z(-3)),Q(Z(2))) # 2 x ^ 3 + x ^ 2 - 3 x + 2
y = Polynomial[Q](Q(Z(1)),Q(Z(0)),Q(Z(1))) # x ^ 2 + 1
print(x // y) # -> 2 x + 1
print(x % y)  # -> -5 x + 1

Puisque $ 2 x ^ 3 + x ^ 2-3 x + 2 = (2 x + 1) (x ^ 2 + 1) + (-5 x + 1) $, le quotient et le reste correspondaient certainement.

Assurez-vous également que «Générique» fonctionne. Essayez de remplacer les crochets «[]» par le corps approprié.

f = Polynomial[FiniteField_11](Q(Z(1)),Q(Z(1)))

Lorsque je le vérifie avec mypy, j'obtiens une erreur indiquant que ce n'est pas le type attendu.

field_extension.py:347: error: Argument 1 to "Polynomial" has incompatible type "Rational"; expected "FiniteField_11"
field_extension.py:347: error: Argument 2 to "Polynomial" has incompatible type "Rational"; expected "FiniteField_11"

Anneau résiduel de polypoly

L'anneau restant $ a \ pmod n $, et $ a $ et $ n $ étaient respectivement des anneaux euclidiens. Puisque l'anneau polymorphe est également un anneau euclidien, vous pouvez créer un anneau en vous concentrant sur le reste.

Pour créer un anneau résiduel polynomial pour un diviseur particulier, remplacez simplement ʻInteger dans l'anneau de classe résiduelle par Polynomial [Q] `.

class MR_x2_1(ModRing): # (mod x ^ 2 + 1)
    def __init__(self: "MR_x2_1", a: Polynomial[Q]) -> None:                                          
        super().__init__(a, Polynomial[Q](Q(Z(1)),Q(Z(0)),Q(Z(1))))                                
                                    
u = MR_x2_1(Polynomial[Q](Q(Z(2)),Q(Z(1)),Q(Z(-3)),Q(Z(2))))
print(u) # -> -5 x + 1 (mod x ^ 2 + 1)

Multiplicateur inverse de l'anneau restant du polypole

Dans le cas d'un anneau de classe de reste entier, si le diviseur est un "nombre premier" qui ne peut pas être davantage factorisé en premier, il s'agit d'un corps fini.

Même pour les polynômes, si le polynôme diviseur est un ** polynôme irréductible ** qui ne peut pas être davantage factorisé dans une plage de champ spécifique, alors il y aura toujours un élément inverse dans l'anneau de reste du polynôme.

Par exemple, dans la plage des nombres rationnels, $ x ^ 2 -1 $ peut être factorisé comme $ (x + 1) (x -1) $, mais $ x ^ 2-1 $ ne peut pas être factorisé. Cependant, si vous l'étendez à la plage de nombres réels, vous pouvez le factoriser en $ (x + \ sqrt 2) (x- \ sqrt 2) $. En d'autres termes, $ x ^ 2 --2 $ est un polymorphisme irréductible dans la gamme des nombres rationnels, mais pas un polymorphisme irréductible dans la gamme des nombres réels.

Maintenant, appliquons Polynomial [Q] au FiniteField précédemment créé tel quel.

class F_x2_n2(FiniteField): # (mod x ^ 2 - 2)
    def __init__(self: "F_x2_n2", a: Polynomial[Q]) -> None:
        super().__init__(a, Polynomial[Q](Q(Z(1)),Q(Z(0)),Q(Z(-2))))

v = F_x2_n2(Polynomial[Q](Q(Z(1)),Q(Z(1)))) # x + 1 (mod x ^ 2 - 2)
print(v.inverse()) # -> x - 1 (mod x ^ 2 - 2)

Allons vérifier.

$ (x + 1) (x --1) = x ^ 2 --1 = (x ^ 2 --2) + 1 $ est plus certainement $ x + 1 \ pmod {x ^ 2 --2} $ $ x --1 \ pmod {x ^ 2 --2} $.

Élargir l'algèbre du corps

$ N $ polymorphisme d'ordre $ f (x) = a_0 + a_1 x + a_2 x ^ 2 + \ cdots + a_n x ^ n $ racine $ x $ avec $ K $ comme élément d'un champ $ K $ Prenons le cas où il n’existe pas. Par exemple, en considérant l'équation quadratique du coefficient rationnel $ f (x) = x ^ 2 --2 = 0 $, la solution $ x $ n'existe pas dans la plage des nombres rationnels.

Dans ce cas, considérer un nouveau $ x $ qui satisfait cette équation et l'ajouter au champ $ K $ pour créer un nouveau champ $ L $ s'appelle ** expansion algébrique **.

Ce $ L $ peut être facilement exprimé en utilisant l'anneau de reste du polypole.

Représenter l'expansion algébrique à l'aide de l'anneau de reste d'un polypoly

Ici, nous avons un polynôme $ g (x) $ et un polynôme de contraction $ f (x) $, et considérons le reste $ r (x) $ obtenu en divisant $ g (x) $ par $ f (x) $ Ensuite, l'équation suivante est vraie.

g(x) = h(x) f(x) + r(x)

Si vous remplacez $ a $, la racine de $ f (x) $, pour $ x $, vous obtenez $ g (a) = r (a) $ à partir de $ f (a) = 0 $.

En d'autres termes, substituer la racine du polynôme irréductible $ f (x) $ dans le polynôme $ g (x) $ est le reste de $ g (x) $ divisé par $ f (x) $. Ce n'est qu'une opération à trouver.

f(a) = 0 \land g(a) = r(a) \longleftrightarrow g(x) \bmod f(x) = r(x)

Regardons un exemple d'ajout de la racine de $ x ^ 2 - 2 $ au champ de nombre rationnel $ Q $.

class L(FiniteField): # Q(√2)
    def __init__(self: "L", a: Polynomial[Q]) -> None:
        super().__init__(a, Polynomial[Q](Q(Z(1)),Q(Z(0)),Q(Z(-2)))) # x^2 - 2

a = L(Polynomial[Q](Q(Z(1)),Q(Z(0))))
print(a * a) # -> 2 (mod x ^ 2 - 2)

Tout d'abord, nous avons créé un champ $ L $ en ajoutant les racines de $ x ^ 2 - 2 $ au champ de nombre rationnel $ Q $, et avons assigné $ x $ sur $ L $ à la variable ʻa`. Si vous mettez ce $ x $ au carré, vous pouvez voir que c'est «2».

En d'autres termes, ʻa` est $ \ pm \ sqrt 2 $. Par conséquent, ce corps $ L $ s'écrit également $ Q (\ sqrt 2) $.

Ajoutons les racines de $ x ^ 2-3 $ pour créer le corps $ M $ (c'est-à-dire $ Q (\ sqrt 2, \ sqrt 3) $). Une chose à noter ici est que les coefficients du polypole doivent être extraits de $ L $.

L_1 = L(Polynomial[Q](Q(Z(1))))#1inQ(√2)
L_3 = L(Polynomial[Q](Q(Z(3))))#3inQ(√2)
L_0 = L(Polynomial[Q](Q(Z(0))))#0inQ(√2)

class M(FiniteField): # Q(√2, √3)
    def __init__(self, a: Polynomial[L]) -> None:
        super().__init__(a, Polynomial[L](L_1,L_0,-L_3))
        #Voici une erreur
        #super().__init__(a, Polynomial[L](Q(Z(1)),Q(Z(0)),-Q(Z(3))))

b = M(Polynomial[L](a))
print(b * b) # -> 2 (mod x ^ 2 - 2) (mod 1 (mod x ^ 2 - 2) x ^ 2 - 3 (mod x ^ 2 - 2))

c = M(Polynomial[L](L_1,L_0))
print(c * c) # -> 3 (mod x ^ 2 - 2) (mod 1 (mod x ^ 2 - 2) x ^ 2 - 3 (mod x ^ 2 - 2))

La sortie est désordonnée, mais j'ai pu créer les équivalents de $ \ pm \ sqrt {2} $ et $ \ pm \ sqrt {3} $ dans la même classe.

Bien sûr, vous pouvez également créer $ \ pm \ sqrt {6} $.

d = b * c
print(d * d) # -> 6 (mod x ^ 2 - 2) (mod 1 (mod x ^ 2 - 2) x ^ 2 - 3 (mod x ^ 2 - 2))

Il convient de noter ici que les nombres ajoutés dans l'expansion algébrique sont dans une dimension différente des éléments du corps d'origine.

Par exemple, $ \ pm \ sqrt {2} $, $ \ pm \ sqrt {3} $ et $ \ pm \ sqrt {6} $ peuvent être comparés chacun par carré, mais $ \ Il n'est pas possible de savoir lequel est le plus grand, pm \ sqrt {2} $ ou $ \ pm \ sqrt {3} $ tels quels.

Résumé

En fait, avant de donner l'indication de type, j'ai eu l'erreur suivante en ajoutant $ \ sqrt {3} $ à $ Q (\ sqrt {2}) $, et j'étais inquiet car je ne pouvais pas comprendre la cause même si je la lisais.

Traceback (most recent call last):
  File "./field_extension.py", line 370, in <module>
    print(c * c) # -> 3 (mod x ^ 2 - 2) (mod 1 (mod x ^ 2 - 2) x ^ 2 - 3 (mod x ^ 2 - 2))
  File "./field_extension.py", line 214, in __mul__
    return self.__class__(self.a * x.a)
  File "./field_extension.py", line 364, in __init__
    super().__init__(a, Polynomial(Q(Z(1)),Q(Z(0)),-Q(Z(3))))
  File "./field_extension.py", line 207, in __init__
    self.a = a % n
  File "./field_extension.py", line 283, in __mod__
    q, r = Polynomial._div_poly(self.coeffs, x.coeffs)
  File "./field_extension.py", line 265, in _div_poly
    q[i] = r[i] / b[0]
  File "./field_extension.py", line 37, in __truediv__
    return self * x.inverse()
  File "./field_extension.py", line 214, in __mul__
    return self.__class__(self.a * x.a)
AttributeError: 'Rational' object has no attribute 'a'

Cependant, lorsque je lui ai donné un indice de type et que je l'ai vérifié avec mypy, cela m'a donné un message très facile à comprendre comme celui ci-dessous.

field_extension.py:364: error: Argument 1 to "Polynomial" has incompatible type "Rational"; expected "L"
field_extension.py:364: error: Argument 2 to "Polynomial" has incompatible type "Rational"; expected "L"
field_extension.py:364: error: Argument 3 to "Polynomial" has incompatible type "Rational"; expected "L"

En parlant bien sûr, c'est une erreur naturelle, mais quand je vous ai dit l'erreur pour la première fois, je me suis dit: "Oh, je vois."

Mypy est un outil qui signale les malentendus et les erreurs résultant de la complexité des programmes.

Recommended Posts

Tapez Python pour implémenter l'expansion algébrique (3) ~ Anneau polygonal, anneau résiduel du polynôme, expansion du champ ~
Tapez Python pour implémenter l'expansion algébrique (2) ~ Champ, champ de nombres rationnels, anneau euclidien, anneau de classe de reste, corps fini ~
Tapez Python pour implémenter l'expansion algébrique (1) ~ Monoïdes, groupes, anneaux, anneaux entiers ~
Implémenter l'extension en Python
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'implémenter la permutation en Python
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé d'implémenter ADALINE en Python
J'ai essayé d'implémenter PPO en Python
Essayez d'implémenter Oni Mai Tsuji Miserable avec python
Comment simplifier l'ajustement polymorphe restreint en python
Comment implémenter la mémoire partagée en Python (mmap.mmap)
J'ai essayé d'implémenter TOPIC MODEL en Python
J'ai essayé d'implémenter le tri sélectif en python
Comment gérer le type datetime dans sqlite3 de python
Mettre en œuvre des recommandations en Python
J'ai essayé d'implémenter un pseudo pachislot en Python
Implémenter XENO avec python
J'ai essayé d'implémenter le poker de Drakue en Python
Implémenter la solution de l'algèbre de Riccati en Python
J'ai essayé d'implémenter GA (algorithme génétique) en Python
Implémenter sum en Python
Convertir le type d'entité Webpay en type Dict (récursivement en Python)
Implémenter Traceroute dans Python 3
Comment implémenter Python EXE pour Windows avec le conteneur Docker
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
Une histoire sur la tentative d'implémentation de variables privées en Python.
J'ai essayé d'implémenter le blackjack du jeu Trump en Python
Pour vider stdout en Python
Connectez-vous au site Web en Python
Implémenter Naive Bayes dans Python 3.3
Implémenter d'anciens chiffrements en python
Implémenter Redis Mutex en Python
Parler avec Python [synthèse vocale]
Mettre en œuvre un RPC rapide en Python
Implémenter l'algorithme de Dijkstra en python
Implémenter le bot de discussion Slack en Python
Comment développer en Python
Publier sur Slack en Python
Spécification du fuseau horaire lors de la conversion d'une chaîne de caractères en type datetime avec python
J'ai écrit une fonction pour charger le script d'extension Git en Python
Implémenter un automate fini déterministe en Python pour déterminer des multiples de 3
J'ai essayé de mettre en œuvre un jeu de dilemme de prisonnier mal compris en Python
Que faire lorsque le type de valeur est ambigu en Python?
Mettre en œuvre l'apprentissage de l'empilement en Python [Kaggle]
[Python] Comment faire PCA avec Python
Implémenter la fonction power.prop.test de R en python
Définition du type d'argument de fonction en python
Entraine toi! !! Introduction au type Python (conseils de type)
Comment collecter des images en Python
Comment utiliser SQLite en Python
Charger dynamiquement les types json avec python
Dans la commande python, python pointe vers python3.8
Implémenter le modèle Singleton en Python
Essayez de calculer Trace en Python
Type spécifié en python. Lancer des exceptions