0454: 四元数¶
問題¶
四元数 \(q = a + bi + cj + dk\) を表すクラス Quaternion を実装してください。
四元数は乗法が可換でない斜体をなし、3D 回転の表現に使われます。
基底 \(i, j, k\) は次の関係を満たします。
\[
i^2 = j^2 = k^2 = ijk = -1
\]
この関係から、積は次のように定まります。
\[
\begin{aligned}
q_1 q_2 = {}& (a_1 a_2 - b_1 b_2 - c_1 c_2 - d_1 d_2) \\
&+ (a_1 b_2 + b_1 a_2 + c_1 d_2 - d_1 c_2)\,i \\
&+ (a_1 c_2 - b_1 d_2 + c_1 a_2 + d_1 b_2)\,j \\
&+ (a_1 d_2 + b_1 c_2 - c_1 b_2 + d_1 a_2)\,k
\end{aligned}
\]
Quaternion(a, b, c, d) は成分 \(a, b, c, d\) の四元数を表します。
次のメソッドを実装します。
__add__:四元数同士の成分ごとの和。__mul__:上式による四元数同士の積。オペランドがスカラー(intまたはFraction)の場合はスカラー倍。__rmul__:2 * qのような左からのスカラー倍。__abs__:ノルム \(\sqrt{a^2 + b^2 + c^2 + d^2}\) をfloatで返します。__eq__:4 成分がすべて等しいときに限りTrueを返します。__repr__:eval()で再作成できるQuaternion(1, -2, 3, 0)の形式を返します。conjugate():共役 \(\bar{q} = a - bi - cj - dk\) を返します。inverse():乗法逆元 \(q^{-1} = \bar{q} / (a^2 + b^2 + c^2 + d^2)\) を返します。
積が非可換であること(i * j と j * i が異なること)を「例」で確認します。
制約¶
- 成分は
intまたはfractions.Fractionに限ります。それ以外の成分はTypeErrorを送出します(floatは許しません)。 - 分母が 1 の
Fraction成分はintに正規化し、reprの出力を安定させます。 - スカラー倍のスカラーも
intまたはFractionに限ります。 - 四元数と
Quaternion以外のオブジェクトとの+や==ではNotImplementedを返します。 - 零四元数の
inverse()は、メッセージzero quaternion has no inverseのZeroDivisionErrorを送出します。
例¶
>>> from fractions import Fraction
>>> i = Quaternion(0, 1, 0, 0)
>>> j = Quaternion(0, 0, 1, 0)
>>> k = Quaternion(0, 0, 0, 1)
>>> i * i
Quaternion(-1, 0, 0, 0)
>>> i * j
Quaternion(0, 0, 0, 1)
>>> j * i
Quaternion(0, 0, 0, -1)
>>> i * j == j * i
False
>>> i * j == k
True
>>> q = Quaternion(1, 2, 3, 4)
>>> q + Quaternion(4, 3, 2, 1)
Quaternion(5, 5, 5, 5)
>>> 2 * q
Quaternion(2, 4, 6, 8)
>>> Fraction(1, 2) * q
Quaternion(Fraction(1, 2), 1, Fraction(3, 2), 2)
>>> q.conjugate()
Quaternion(1, -2, -3, -4)
>>> abs(Quaternion(1, 1, 1, 1))
2.0
>>> q.inverse()
Quaternion(Fraction(1, 30), Fraction(-1, 15), Fraction(-1, 10), Fraction(-2, 15))
>>> q * q.inverse()
Quaternion(1, 0, 0, 0)
>>> Quaternion(0, 0, 0, 0).inverse()
Traceback (most recent call last):
...
ZeroDivisionError: zero quaternion has no inverse
発展¶
__sub__と単項の__neg__を追加してください。__truediv__をq1 * q2.inverse()として定義してください。積が非可換なので、「右から割る」と「左から割る」が異なることを例で確認してください。- 単位四元数 \(q\) による回転 \(v \mapsto q v q^{-1}\) を実装し、\(q = \frac{1}{\sqrt{2}}(1 + i)\) に相当する回転が \(y\) 軸を \(z\) 軸に写すことを確認してください(ノルムが無理数にならないよう、係数のかけ方を工夫します)。
参考¶
- 『Python Distilled』第4章「数値プロトコル」
- 『Python Distilled』第4章「プロトコルとデータの抽象化」
- 四元数 - Wikipedia