首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何实现四元数的运算

如何实现四元数的运算

作者头像
老齐
发布2020-10-27 16:15:49
1.1K0
发布2020-10-27 16:15:49
举报
文章被收录于专栏:老齐教室老齐教室

与本文相关图书:《Python大学实用教程》


在前面的一篇文章《Python中的5对必知的魔法方法》中所介绍的“魔法方法”,或者说是特殊方法,其命名均是双下划线开始和结束。英文中称为“dunder methods”。为了更充分理解这类方法,本文通过一个示例,专门介绍此类方法的特点。

构建四元数对象

四元数是一个代数概念,通常用于描述旋转,特别是在3D建模和游戏中有广泛的应用。

Q = w + x\hat i + y\hat j + z\hat k

其中

w,x, y, z \in \mathbb R,\hat i^2 + \hat j^2 +\hat k^2 = -1

下面就一步一步演示此对象的创建方法,特别要关注双下划线开始和结束的那些特殊方法。

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z

这是首先定义了__init__初始化方法,通过这个方法,在创建实例时将

w,x,y,x

分别定义为实数。

>>> q1 = Quaternion(1, 2, 3, 4)
>>> q1
<__main__.Quaternion at 0x7f4210f483c8>

貌似一个四元数对象定义了。但是,它没有友好的输出。接下来可以通过定义__repr____str__,让它对机器或者开发者都更友好。继续在类中增加如下方法:

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)

对于这两方法,在前面的《Python中的5对必知的魔法方法》中已经有介绍,请参考。

>>> q1          # calls q1.__repr__
Quaternion(1, 2, 3, 4)

>>> print(q1)   # calls q1.__str__
Q = 1.00 + 2.00i + 3.00j + 4.00k

实现代数运算

上述类已经能实现实例化了,但是,该对象还要参与计算,比如加、减法等。这类运算如何实现?

加法

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)
    
    def __add__(self, other):
        w = self.w + other.w
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        return Quaternion(w, x, y, z)

__add__是用于实现对象的加法运算的特殊方法。这样就可以使用+运算符了:

>>> q1 = Quaternion(1, 2, 3, 4)
>>> q2 = Quaternion(0, 1, 3, 5)
>>> q1 + q2
Quaternion(1, 3, 6, 9)

减法

类似地,通过__sub__实现减法运算,不过,这次用一行代码实现。

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)
    
    def __add__(self, other):
        w = self.w + other.w
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        return Quaternion(w, x, y, z)
        
    def __sub__(self, other):
        return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))

有点酷。这里使用了实例对象的__dict__属性,它以字典形式包含了实例的所有属性,请参考《Python大学实用教程》中的详细讲解。

乘法

乘法,如果了解一下线性代数,会感觉有点复杂。其中常见的一个是“点积”,自从Python3.5以后,用@符号调用__matmul__方法实现,对于四元数对象而言不能,就是元素与元素对应相乘。

对于四元数而言——本质就是向量,也可以说是矩阵,其乘法就跟矩阵乘法类似,比如,同样不遵守互换率:

Q_1Q_2 \ne Q_2Q_1

 。

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)
    
    def __add__(self, other):
        w = self.w + other.w
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        return Quaternion(w, x, y, z)
        
    def __sub__(self, other):
        return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
        
    def __mul__(self, other):
        if isinstance(other, Quaternion):
            w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
            x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
            y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
            z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
            return Quaternion(w, x, y, z)
        elif isinstance(other, (int, float)):
            return Quaternion(*[other * i for i in self.__dict__.values()])
        else:
            raise TypeError("Operation undefined.")

__mul__方法中,如果other引用一个四元数对象,那么就会计算Hamilton积,并返回一个新的对象;如果other是一个标量(比如整数),就会与四元数对象中的每个元素相乘。

如前所述,四元数的乘法不遵循交换律,但是,如果执行2 * q1这样的操作,按照上面的方式,会报错——在上面的__mul__方法中解决了q1 * 2的运算,而一般我们认为这两个计算是相同的。为此,定义了·rmul·来解决此问题:

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)
    
    def __add__(self, other):
        w = self.w + other.w
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        return Quaternion(w, x, y, z)
        
    def __sub__(self, other):
        return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
        
    def __mul__(self, other):
        if isinstance(other, Quaternion):
            w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
            x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
            y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
            z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
            return Quaternion(w, x, y, z)
        elif isinstance(other, (int, float)):
            return Quaternion(*[other * i for i in self.__dict__.values()])
        else:
            raise TypeError("Operation undefined.")
            
    def __rmul__(self, other):
        if isinstance(other, (int, float)):
            return self.__mul__(other)
        else:
            raise TypeError("Operation undefined.")

相等

比较两个四元数是否相等,可以通过定义__eq__方法来实现。

class Quaternion:
    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
    
    def __repr__(self):
        return "Quaternion({}, {}, {}, {})".format(
            self.w, self.x, self.y, self.z)

    def __str__(self):
        return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
            self.w, self.x, self.y, self.z)
    
    def __add__(self, other):
        w = self.w + other.w
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        return Quaternion(w, x, y, z)
        
    def __sub__(self, other):
        return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
        
    def __mul__(self, other):
        if isinstance(other, Quaternion):
            w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
            x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
            y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
            z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
            return Quaternion(w, x, y, z)
        elif isinstance(other, (int, float)):
            return Quaternion(*[other * i for i in self.__dict__.values()])
        else:
            raise TypeError("Operation undefined.")
            
    def __rmul__(self, other):
        if isinstance(other, (int, float)):
            return self.__mul__(other)
        else:
            raise TypeError("Operation undefined.")
            
    def __eq__(self, other):
        r = list(map(lambda i, j: abs(i) == abs(j), self.__dict__.values(), other.__dict__.values()))
        return sum(r) == len(r) 

其他运算

下面的方法,也可以接续到前面的类中,不过就不是特殊放方法了。

from math import sqrt


def norm(self):
    return sqrt(sum([i**2 for i in self.__dict__.values()))

def conjugate(self):
    x, y, z = -self.x, -self.y, -self.z
    return Quaterion(self.w, x, y, z)

def normalize(self):
    norm = self.norm()
    return Quaternion(*[i / norm for in self.__dict__.values()])

def inverse(self):
    qconj = self.conjugate()
    norm  = self.norm()
    return Quaternion(*[i / norm for i in qconj.__dict__.values()])

用这些方法实现了各自常见的操作。

通过本文的这个示例,可以更深刻理解双下划线的方法为编程带来的便捷。


关注微信公众号:老齐教室

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 构建四元数对象
  • 实现代数运算
    • 加法
      • 减法
        • 乘法
          • 相等
            • 其他运算
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档