Írjunk egy osztályt, mely a kvaterniók matematikai tulajdonságait valósítja meg.
Mivel néhány funkcióját használni fogjuk, meghívjuk a pylab csomagot.
%pylab inline
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:
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.
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!
kv1 = quatr(1,2,3,4)
kv2 = quatr(2,-1,5,-3)
print(kv1 + kv2)
print(kv1*kv2)
print(kv2*kv1)
print(kv1.inv())
print(kv1*kv1.inv())
print(kv1==kv2)
print(kv1==kv1)