☠ 8. Feladat

Kvaterniók

Írjunk egy osztályt, mely a kvaterniók matematikai tulajdonságait valósítja meg.

Megoldás

Mivel néhány funkcióját használni fogjuk, meghívjuk a pylab csomagot.

In [1]:
%pylab inline 
Populating the interactive namespace from numpy and matplotlib

Egy kvaterniót négy valós szám határoz meg, amelyek a báziskvaterniók ($1$, $i$, $j$ és $k$) együtthatói, így egy tetszőleges kvaternió felírható ilyen alakban: $q = a + b i + c j + d k$. Ez alapján a kvaterniók egy négydimenziós lineáris teret alkotnak, így értelmezhető két kvaternió összege: $$\begin{split} q_1 + q_2 &= \left( a_1 + b_1 i + c_1 j + d_1 k \right) + \left( a_2 + b_2 i + c_2 j + d_2 k \right) ={} \\ &= a_1 + a_2 + \left( b_1 + b_2 \right) i + \left( c_1 + c_2 \right) j + \left( d_1 + d_2 \right) k \end{split}$$

A kvaterniók a komplex számokhoz hasonlóan Testet is alkotnak, így az egymással valós szorzás is értelmezhető rajtuk, ehhez azonban elegendő a báziskvaterniók szorzatát definiálni, a többi szorzat eredménye ezekből következik. A kvaterniók szorzása nem kommutatív, így a báziskvaterniók minden szorzás-variációját definiálni kell az alábbi táblázat szerint: szorzástábla Ez alapján már kiszámítható két tetszőleges kvaternió szorzata: $$\begin{split} q_1 \cdot q_2 &= \left(a_1 + b_1 i + c_1 j + d_1 k\right) \cdot \left(a_2 + b_2 i + c_2 j + d_2 k\right) ={} \\ &= a_1 a_2 - b_1 b_2 - c_1 c_2 - d_1 d_2 + \left( a_1 b_2 + b_1 a_2 + c_1 d_2 - d_1 c_2 \right) i +{} \\ &\quad + \left( a_1 c_2 + c_1 a_2 - b_1 d_2 + d_1 b_2 \right) j + \left( a_1 d_2 + d_1 a_2 + v_1 c_2 - c_1 b_2 \right) k \end{split}$$

Ezek alapján, a kvaterniókat képviselő osztályban már létre definiálni lehet az összeadásért és a szorzásért felelős __add__ és __mul__ függvényeket, sőt a kivonásért felelős __sub__ függvényt is el lehet készíteni. A negyedik alapműveletet, az osztást már nem lehet egyértelműen definiálni, mert algebrailag nem egyértelmű. Az osztás művelet általában az osztó inverzével való szorzást jelent, de mivel jelen esetben a szorzás nem kommutatív, nem mindegy, hogy jobb vagy bal oldali osztást veszünk. A legegyszerűbb ezt a problémát kikerülni, ha létrehozunk egy függvényt, ami kiszámolja egy adott kvaternió inverzét, és azt, hogy ezzel milyen irányból szoroz, a felhasználóra bízzuk.

Az inverz előtt, azonban érdemes bevezetni a komplexekhez hasonló, konjugált fogalmát: egy kvaternió szám konjugáltja legyen az a kvaternió, amelynek valós része megegyezik, vele, de képzetes része ellentett, vagyis $q^* = \left( a + b i + c j + d k \right)^* = a - b i - c j - d k$. A szorzás képletének felhasználásával belátható, hogy egy kvaterniót megszorozva a konjugáltjával, egy valós számot kapunk: $$ q \cdot q^* = \left( a + b i + c j + d k \right) \cdot \left( a - b i - c j - d k \right) = a^2 + b^2 + c^2 + d^2 $$ A komplexekhez hasonlóan, ez legyen a kvaternió abszolútértékének négyzete: $ q \cdot q^* = \left| q \right|^2$. Belátható, hogy így egy tetszőleges kvaternió inverze: $$ q^{-1} = \frac{q^*}{ \left| q \right| ^2 } $$ így tehát, az osztás műveletet letudtuk, tehát teljes körűen lehet számolni a kvaterniókkal.

In [2]:
class quatr(object):
    '''A kvaterniók matematikai tulajdonságait megvalósító osztály'''
    
    def __init__(self, real=0, i=0, j=0, k=0):
        '''Az inicializáláshoz meg kell adni a báziskvaterniók együtthatóit'''
        self.real = real
        self.i = i
        self.j = j
        self.k = k
        
    def conjugate(self):
        '''Függvény, amely visszaadja a kvaternió konjugáltját'''
        return quatr(self.real, -self.i, -self.j, -self.k)
    
    def imag(self):
        '''Függvény, amely visszaadja a kvaternió képzetes részét'''
        return quatr(0, self.i, self.j, self.k)
    
    def inv(self):
        '''Függvény, amely visszaadja a kvaternió reciprokát'''
        return quatr(self.conjugate().real/abs(self)**2, self.conjugate().i/abs(self)**2, self.conjugate().j/abs(self)**2, self.conjugate().k/abs(self)**2)
    
    def __abs__(self):
        '''Visszaadja a kvaternió abszolútértékét'''
        return sqrt((self*conjugate(self)).real)
    
    def __add__(self, other):
        '''Összead két kvaterniót'''
        return quatr(self.real + other.real, self.i + other.i, self.j + other.j, self.k + other.k)
    
    def __eq__(self, other):
        '''Összehasonlít két kvaterniót'''
        if self.real == other.real and self.i == other.i and self.j == other.j and self.k == other.k:
            return True
        return False
    
    def __mul__(self, other):
        '''Összeszoroz két kvaterniót'''
        ujreal = self.real*other.real - self.i*other.i - self.j*other.j - self.k*other.k
        uji = self.i*other.real + self.real*other.i + self.j*other.k - self.k*other.j
        ujj = self.real*other.j + self.j*other.real - self.i*other.k + self.k*other.i
        ujk = self.real*other.k + self.k*other.real + self.i*other.j - self.j*other.i
        return quatr(ujreal, uji, ujj, ujk)
       
    def __sub__(self, other):
        '''Kivon egymásból két kvaterniót'''
        return quatr(self.real - other.real, self.i - other.i, self.j - other.j, self.k - other.k)

    def __repr__(self): # Erre a függvényre azért van szükség, hogy ha meg akarjuk nézni egy kvaternió típusú változó értékét, akkor az számunkra is értelmezhető módon meg tudjuk tenni.
        p1 = "+"
        p2 = "+"
        p3 = "+"
        if self.i < 0:
            p1 = "-"
        if self.j < 0:
            p2 = "-"
        if self.k < 0:
            p3 = "-"
        return "{0} {1} {2}i {3} {4}j {5} {6}k".format(abs(self.real), p1, abs(self.i), p2, abs(self.j), p3, abs(self.k))

Hogy ellenőrizzük az osztályunk működését, hozzunk létre pár kvaterniót, és végezzünk velük néháány műveletet!

In [3]:
kv1 = quatr(1,2,3,4)
kv2 = quatr(2,-1,5,-3)
In [4]:
print(kv1 + kv2)
print(kv1*kv2)
print(kv2*kv1)
print(kv1.inv())
print(kv1*kv1.inv())
print(kv1==kv2)
print(kv1==kv1)
3 + 1i + 8j + 1k
1 - 26i + 13j + 18k
1 + 32i + 9j - 8k
0.03333333333333333 - 0.06666666666666667i - 0.1j - 0.13333333333333333k
1.0 + 0.0i + 0.0j + 0.0k
False
True