最近,我一直很喜欢从坐标渲染图表和图形,并且我对使用矩阵来转换坐标空间很着迷。

我已经能够成功地缩放和反转二维坐标空间,但现在我的胃口被激发了。:)

我在哪里可以找到有关矩阵、矩阵数学(尤其是适用于 2 维和 3 维空间)的清晰、信息丰富(免费)的教育材料?

有帮助吗?

解决方案

原答案: 我不确定您是否会喜欢数学课程通常如何介绍矩阵。作为一名程序员,您可能会更乐意阅读一本像样的 3D 图形书。它当然应该有非常具体的 3x3 矩阵。还找出那些可以教你的 投影变换 (射影几何是低维几何中一个非常美丽的领域,并且易于编程)。

使用 Python 3 进行矩阵数学迷你课程

内容:

  1. 矩阵 [Vector, __add__, reflect_y, rotate, dilate, transform]
  2. 矩阵:超载 [Matrix, __add__, __str__, __mul__, zero, det, inv, __pow__]
  3. 奖金:复数
  4. 矩阵:(R)进化. 。已经在制作中了(最后有总结)

前言: 根据我的教学经验,我认为别人参考的课程非常好 培训班. 。这意味着,如果您的目标是像数学家一样理解矩阵,那么您无论如何都应该学习整个课程。但如果你的目标比较温和,这是我尝试更适合你的需求的东西(但仍然是为了传达许多理论概念而编写的,这有点与我最初的建议相矛盾。)

如何使用:

  • 这篇文章很长。您可以考虑打印此内容并缓慢进行,例如一天一份。
  • 代码是必不可少的。这是一门针对程序员的课程。练习也是必不可少的。
  • 你应该 看看代码伴侣 其中包含所有这些代码以及更多内容
  • 这是“1 件的价格 2 件”的特别优惠:您还可以在这里学习 Python 3。还有复数。
  • 我将高度重视任何阅读本文的尝试(我正式有资格获得有史以来最长的帖子吗?),因此,如果您不明白某些内容(如果您理解了),请随时发表评论。

1.矩阵

向量

在矩阵之前是向量。您肯定知道如何处理 2 维和 3 维向量:

class Vector:
    """This will be a simple 2-dimensional vector.

    In case you never encountered Python before, this string is a
    comment I can put on the definition of the class or any function.
    It's just one of many cool features of Python, so learn it here!

    """

    def __init__(self, x, y): 
        self.x = x
        self.y = y

现在你可以写

v = Vector(5, 3)
w = Vector(7, -1)

但它本身并没有多大乐趣。让我们添加更多有用的方法:

    def __str__(self: 'vector') -> 'readable form of vector':
        return '({0}, {1})'.format(self.x, self.y)

    def __add__(self:'vector', v: 'another vector') -> 'their sum':
        return Vector(self.x + v.x, self.y + v.y)

    def __mul__(self:'vector', number: 'a real number') -> 'vector':
        '''Multiplies the vector by a number'''
        return Vector(self.x * number, self.y * number)

这让事情变得更有趣,我们现在可以写:

print(v + w * 2)

并得到答案 (19, 1) 很好地打印为向量(如果这些示例看起来不熟悉,请想想这段代码在 C++ 中的样子)。

转换

现在能够写作一切都很酷 1274 * w 但您需要对图形进行更多矢量操作。这里是其中的一些:你可以翻转向量 (0,0) 点,你可以反映它周围 x 或者 y 轴,您可以顺时针或逆时针旋转它(最好在这里画图)。

我们来做一些简单的操作:

    ...

    def flip(self:'vector') -> 'vector flipped around 0':
        return Vector(-self.x, -self.y)

    def reflect_x(self:'vector') -> 'vector reflected around x axis':
        return Vector(self.x, -self.y)


print(v.flip(), v.reflect_x())
  • 问题: 是否可以表达 flip(...) 使用我下面的操作?关于什么 reflect_x?

现在你可能想知道为什么我省略了 reflect_y. 。嗯,这是因为我想让你停下来写一下你自己的版本。好的,这是我的:

    def reflect_y(self:'vector') -> 'vector reflected around y axis':
        return self.flip().reflect_x()

看,如果你看看这个函数是如何计算的,它实际上是非常简单的。但突然发生了一件惊人的事情:我能够仅使用现有的转换来编写转换 flipreflect_x. 。对于我所关心的一切, reflect_y 可以在派生类中定义而无需访问 xy 它仍然有效!

数学家将这些函数称为 运营商. 。他们会说 reflect_y 是一个由下式获得的运算符 作品 运营商数量 flipreflect_x 这是由 reflect_y = flip ○ reflect_x (你应该看到小圆圈,一个 Unicode 符号 25CB).

  • 笔记: 我会非常自由地使用 = 符号从现在开始表示两个操作产生相同的结果,如上段所示。这是一个“数学 =“, 哪个 不能用程序来表达.

所以如果我这样做

print(v.reflect_y())

我得到结果 (-5, 3). 。去想象一下吧!

  • 问题: 考虑一个组合 reflect_y ◦ reflect_y. 。你会怎么命名它?

旋转次数

这些操作很好而且很有用,但您可能想知道为什么引入轮换这么慢。好的,我开始了:

    def rotate(self:'vector', angle:'rotation angle') -> 'vector':
        ??????

此时,如果你知道如何旋转向量,你应该继续填写问号。否则请耐心听我讲一个更简单的案例:逆时针旋转 90 度。在一张纸上画这个并不难:

    def rotate_90(self:'vector') -> 'rotated vector':
        new_x = - self.y
        new_y =   self.x
        return Vector(new_x, new_y)

x_axis = Vector(1, 0)
y_axis = Vector(0, 1)

print(x_axis.rotate_90(), y_axis.rotate_90())

现在给出 (0, 1) (-1, 0). 。自己运行吧!

  • 问题: 证明 flip = rotate_90 ◦ rotate_90.

无论如何,我不会再隐藏秘密成分了:

import math   # we'll need math from now on
  ...

class Vector:

      ...

    def rotate(self:'vector', angle:'rotation angle') -> 'rotated vector':
        cos = math.cos(angle)
        sin = math.sin(angle)
        new_x = cos * self.x - sin * self.y
        new_y = sin * self.x + cos * self.y
        return Vector(new_x, new_y)

现在让我们尝试一下:

print(x_axis.rotate(90), y_axis.rotate(90))

如果您期望与以前相同的结果, (0, 1) (-1, 0), ,你一定会失望的。该代码打印:

(-0.448073616129, 0.893996663601) (-0.893996663601, -0.448073616129)

男孩,它丑吗?

  • 符号: 我会说我们应用了操作 rotate(90)x 在上面的例子中。我们获得的知识是 rotate(90) != rotate_90.

  • 问题: 这里发生了什么?如何表达 rotate_90 按照 rotate?如何表达 flip 按照 rotate?

扩张

这些旋转当然很有用,但它们并不是您完成 2D 图形所需的一切。考虑以下转换:

    def dilate(self:'vector', axe_x:'x dilation', axe_y:'y dilation'):
        '''Dilates a vector along the x and y axes'''
        new_x = axe_x * self.x
        new_y = axe_y * self.y
        return Vector(new_x, new_y)

dilate 事物扩张了 xy 轴以可能不同的方式。

  • 锻炼: 填写问号处 dilate(?, ?) = flip, dilate(?, ?) = reflect_x.

我会用这个 dilate 函数来演示数学家所说的东西 交换律:也就是说,对于每个参数值 a, b, c, d 你可以确定

dilate(a, b) ◦ dilate(c, d) = dilate(c, d) ◦ dilate(a, b)
  • 锻炼: 证明给我看。另外,对于所有可能的参数值,下面的值是否都成立?

    • rotate(a) ◦ rotate(b) = rotate(b) ◦ rotate(a)
    • dilate(a, b) ◦ rotate(c) = rotate(c) ◦ dilate(a, b)
    • rotate(a) ◦ __mul__(b) = __mul__(b) ◦ rotate(a)

矩阵

让我们总结一下我们这里拥有的所有东西,我们的 向量上的运算符 x

  • flip, reflect_x, *, rotate(angle), dilate(x, y)

从中可以做出一些非常疯狂的东西,比如

  • flip ◦ rotate(angle) ◦ dilate(x, y) ◦ rotate(angle_2) ◦ reflect_y + reflect_x = ???

当您创建越来越复杂的表达式时,人们会希望某种顺序能够突然将所有可能的表达式简化为有用的形式。不要害怕!神奇的是,上述形式的每个表达式都可以简化为

    def ???(self:'vector', parameters):
        '''A magical representation of a crazy function'''
        new_x = ? * self.x + ? * self.y
        new_y = ? * self.x + ? * self.y
        return Vector(new_x, new_y)

用一些数字和/或参数代替 ?s。

  • 例子: 弄清楚“?”的价值是给 __mul__(2) ◦ rotate(pi/4)
  • 另一个例子: 同样的问题 dilate(x, y) ◦ rotate(pi/4)

这使我们能够编写通用函数

    def transform(self:'vector', m:'matrix') -> 'new vector':
        new_x = m[0] * self.x + m[1] * self.y
        new_y = m[2] * self.x + m[3] * self.y
        return Vector(new_x, new_y)

这将采用任意 4 元组数字,称为 矩阵, , 和 申请 它到向量 x. 。这是一个例子:

rotation_90_matrix = (0, -1, 1, 0)
print(v, v.rotate_90(), v.transform(rotation_90_matrix))

打印 (5, 3) (-3, 5) (-3, 5). 。请注意,如果您申请 transform 使用任何原始矩阵,您仍然会得到原始:

origin = Vector(0, 0)
print(origin.transform(rotation_90_matrix))
  • 锻炼: 元组是什么 m 描述了 flip, dilate(x, y), rotate(angle)?

当我们与 Vector 课堂上,这里为那些想要测试向量数学知识和 Python 技能的人提供了一个练习:

  • 最后一战: 添加到 Vector 对您能想到的所有向量运算进行分类(您可以为向量重载多少个标准运算符?看看我的回答)。

2.矩阵:超载

正如我们在上一节中发现的,矩阵可以被认为是一种简写,它允许我们以简单的方式对向量运算进行编码。例如, rotation_90_matrix 编码旋转 90 度。

矩阵对象

现在,当我们将注意力从向量转移到矩阵时,我们也应该在矩阵中也有一个课程。此外,在该函数中 Vector.transform(...) 上面对矩阵的作用有些歪曲。更常见的是 m 当向量变化时要固定,所以从现在开始我们的转换将是矩阵类的方法:

class Matrix:

    def __init__(self:'new matrix', m:'matrix data'):
        '''Create a new matrix.

        So far a matrix for us is just a 4-tuple, but the action
        will get hotter once The (R)evolution happens!

        '''
        self.m = m

    def __call__(self:'matrix', v:'vector'):
        new_x = self.m[0] * v.x + self.m[1] * v.y
        new_y = self.m[2] * v.x + self.m[3] * v.y
        return Vector(new_x, new_y)

如果你不懂Python, __call__ 超载的意义 (...) 对于矩阵,所以我可以使用矩阵的标准表示法 表演 在一个向量上。此外,矩阵通常使用单个大写字母编写:

J = Matrix(rotation_90_matrix)
print(w, 'rotated is', J(w))
  • 锻炼: 使用上一个练习中的矩阵重复此示例。

添加

现在,让我们看看我们还能用矩阵做什么。记住那个矩阵 m 实际上只是对向量上的操作进行编码的一种方法。请注意,对于两个函数 m1(x)m2(x) 我可以创建一个新函数(使用 拉姆达表示法) m = lambda x: m1(x) + m2(x). 。事实证明如果 m1m2 由矩阵表示, 你也可以编码这个 m 使用矩阵!

  • 锻炼: 仔细考虑一下您在执行此声明时可能遇到的任何困难。

您只需添加其数据,例如 (0, 1, -1, 0) + (0, 1, -1, 0) = (0, 2, -2, 0). 。以下是如何在 Python 中添加两个元组,其中使用了一些非常有用且高度 Pythonic 的技术:

    def __add__(self:'matrix', snd:'another matrix'):
        """This will add two matrix arguments.

        snd is a standard notation for second argument.
        (i for i in array) is Python's powerful list comprehension.
        zip(a, b) is used to iterate over two sequences together

        """

        new_m = tuple(i + j for i, j in zip(self.m, snd.m))
        return Matrix(new_m)

现在我们可以写这样的表达式 J + J 甚至 J + J + J, ,但要查看结果,我们必须弄清楚如何打印矩阵。一种可能的方法是打印 4 元组的数字,但让我们从 Matrix.__call__ 函数将数字组织成一个 2x2 堵塞:

    def as_block(self:'matrix') -> '2-line string':
        """Prints the matrix as a 2x2 block.

        This function is a simple one without any advanced formatting.
        Writing a better one is an exercise.

        """

        return ('| {0} {1} |\n' .format(self.m[0], self.m[1]) +
                '| {0} {1} |\n' .format(self.m[2], self.m[3]) )

如果您实际查看此函数,您会发现还有一些改进的空间:

print((J + J + J).as_block())
  • 锻炼: 写一个更好的函数 Matrix.__str__ 这将围绕数字并在固定长度的字段中打印它们。

现在您应该能够编写旋转矩阵:

def R(a: 'angle') -> 'matrix of rotation by a':
    cos = math.cos(a)
    sin = math.sin(a)
    m = ( ????? )
    return Matrix(m)
  • 锻炼: 检查代码 Vector.rotate(self, angle) 并填写问号。测试用

    from math import pi        
    print(R(pi/4) + R(-pi/4))
    

乘法

对于单参数函数,我们能做的最重要的事情就是组合它们: f = lambda v: f1(f2(v)). 。如何用矩阵来反映它?这就需要我们去考察如何 Matrix(m1) ( Matrix(m2) (v)) 作品。如果你扩展它,你会发现

m(v).x = m1[0] * (m2[0]*v.x + m2[1]*v.y) + m1[1] * (m2[2]*v.x + m2[3]*v.y)

类似地对于 m(v).y, ,如果您打开括号,看起来与 Matrix.__call__ 使用新元组 m, ,使得 m[0] = m1[0] * m2[0] + m1[2] * m2[2]. 。因此,让我们将此作为新定义的提示:

    def compose(self:'matrix', snd:'another matrix'):
        """Returns a matrix that corresponds to composition of operators"""

        new_m = (self.m[0] * snd.m[0] + self.m[1] * snd.m[2],
                 self.m[0] * snd.m[1] + self.m[1] * snd.m[3],
                 ???,
                 ???) 
        return Matrix(new_m)
  • 锻炼: 此处填写问号。测试一下

    print(R(1).compose(R(2)))
    print(R(3))
    
  • 数学练习: 证明 R(a).compose(R(b)) 总是一样 R(a + b).

现在让我实话实说:这 compose 函数实际上是数学家决定的 矩阵。作为一种表示法,这是有意义的: A * B 是描述运算符的矩阵 A ○ B, ,正如我们接下来将看到的,将其称为“乘法”还有更深层次的理由。

要开始在 Python 中使用乘法,我们所要做的就是将其排序为 Matrix班级:

    class Matrix:

          ...

        __mul__ = compose
  • 锻炼: 计算 (R(pi/2) + R(pi)) * (R(-pi/2) + R(pi)). 。首先尝试自己在一张纸上找到答案。

规则 +*

让我们为对应的矩阵起一个好名字 dilate(a, b) 操作员。现在没有什么问题了 D(a, b), ,但我会借此机会引入标准符号:

def diag(a: 'number', b: 'number') -> 'diagonal 2x2 matrix':
    m = (a, 0, 0, b)
    return Matrix(m)

尝试 print(diag(2, 12345)) 看看为什么它被称为 对角线 矩阵。

由于之前发现运算的组合并不总是可交换的, * 对于矩阵来说,运算符也不总是可交换的。

  • 锻炼: 返回并刷新 交换律 如果有必要的话。然后给出矩阵的例子 A, B, ,由 Rdiag,这样 A * B 不等于 B * A.

这有点奇怪,因为数字的乘法总是可交换的,并提出了一个问题: compose 确实名副其实 __mul__. 。这里有很多规则 +* 满足:

  1. A + B = B + A
  2. A * (B + C) = A * B + A * C
  3. (A + B) * C = A * C + B * C
  4. (A * B) * C = A * (B * C)
  5. 有一种操作叫做 A - B(A - B) + B = A

    • 锻炼: 证明这些陈述。如何定义 A - B 按照 +, *diag?什么是 A - A 等于?添加方法 __sub__ 到班级 Matrix. 。如果你计算会发生什么 R(2) - R(1)*R(1)?它应该等于什么?

(A * B) * C = A * (B * C) 平等被称为 关联性 而且特别好,因为这意味着我们不必担心将括号的表达方式放置 A * B * C:

print(R(1) * (diag(2,3) * R(2)))
print((R(1) * diag(2,3)) * R(2))

让我们找到常规数字的类似物 01 和减法:

zero = diag(0, 0)
one = diag(1, 1)     

添加了以下易于验证的内容:

  1. A + zero = A
  2. A * zero = zero
  3. A * one = one * A = A

规则变得完整,因为它们有一个简称: 环公理。因此,数学家会说矩阵形成 戒指, ,而且他们确实总是使用符号 +* 当谈论戒指时,我们也应该如此。

使用这些规则可以轻松计算上一节中的表达式:

(R(pi/2) + R(pi)) * (R(-pi/2) + R(pi)) = R(pi/2) * R(-pi/2) +  ... = one + ...
  • 锻炼: 完成这个。证明 (R(a) + R(b)) * (R(a) - R(b)) = R(2a) - R(2b).

仿射变换

是时候回到我们如何定义矩阵了:它们是您可以使用向量执行的某些操作的快捷方式,因此您可以实际绘制它们。您可能需要拿笔或查看其他人建议的材料来查看不同平面变换的示例。

在这些转变中,我们将寻找 仿射 那些看起来到处“一样”的人(没有弯曲)。例如,围绕某个点旋转 (x, y) 符合资格。现在这个不能表达为 lambda v: A(v), ,但可以写成以下形式 lambda v: A(v) + b 对于某些矩阵 A 和矢量 b.

  • 锻炼: 找出 Ab 这样的旋转 pi/2 围绕点 (1, 0) 有上面的形式。它们是独一无二的吗?

请注意,对于每个向量都有一个仿射变换,它是 转移 由向量。

仿射变换可以拉伸或膨胀形状,但它在任何地方都应该以相同的方式进行。现在我希望你相信任何图形的面积在变换下都会改变一个常数。对于矩阵给出的变换 A 这个系数称为 行列式A 可以将面积公式应用到两个向量来计算 A(x_axis)A(y_axis):

    def det(self: 'matrix') -> 'determinant of a matrix':
        return self.m[0]*self.m[3] - self.m[1] * self.m[2]

作为健全性检查, diag(a, b).det() 等于 a * b.

  • 锻炼: 检查一下。当参数之一为 0 时会发生什么?什么时候是负数?

正如你所看到的,旋转矩阵的行列式总是相同的:

from random import random
r = R(random())
print (r, 'det =', r.det())

一件有趣的事情 det 是它是乘法的(如果你冥想足够长的时间,它可以从定义中得出):

A = Matrix((1, 2, -3, 0))
B = Matrix((4, 1, 1, 2))
print(A.det(), '*', B.det(), 'should be', (A * B).det())

使用矩阵可以做的一件有用的事情是编写两个线性方程组

A.m[0]*v.x + A.m[1]*v.y = b.x
A.m[2]*v.x + A.m[3]*v.y = b.y

用更简单的方式: A(v) = b. 。让我们解决他们在(一些)高中教学的系统:将第一个方程乘以 A.m[3], ,第二个 -A.m1 并添加(如果有疑问,请在纸上执行此操作)以解决 v.x.

如果你真的尝试过,你应该得到 A.det() * v.x = (A.m[3]) * b.x + (-A.m[1]) * b.y, ,这表明你总是可以得到 v 通过乘法 b 通过一些其他矩阵。这个矩阵称为 A:

    def inv(self: 'matrix') -> 'inverse matrix':
        '''This function returns an inverse matrix when it exists,
        or raises ZeroDivisionError when it doesn't. 

        '''
        new_m = ( self.m[3] / self.det(), -self.m[1] / self.det(),
                  ????? )
        return Matrix(new_m)

正如您所看到的,当矩阵的行列式为零时,此方法会严重失败。如果你真的想要的话,你可以通过以下方式来满足这种期望:

try:
    print(zero.inv())
except ZeroDivisionError as e: ...
  • 锻炼: 完成该方法。证明逆矩阵不存在 self.det() == 0. 。编写矩阵划分方法并进行测试。使用逆矩阵求解方程 A(v) = x_axis (A 已在上面定义)。

权力

逆矩阵的主要性质是 A * A.inv() 总是等于 one

  • 锻炼: 你自己检查一下。从逆矩阵的定义解释为什么会这样。

这就是为什么数学家表示 A.inv() 经过 A-1. 。我们写一个不错的功能以使用 A ** n 符号为 An?请注意,天真的 for i in range(n): answer *= self 循环是o(| n |),这肯定太慢了,因为这可以用 log |n|:

    def __pow__(self: 'matrix', n:'integer') -> 'n-th power':
        '''This function returns n-th power of the matrix.

        It does it more efficiently than a simple for cycle. A
        while loop goes over all bits of n, multiplying answer
        by self ** (2 ** k) whenever it encounters a set bit.

        ...
  • 锻炼: 在此功能中填写详细信息。测试一下

    X, Y = A ** 5, A ** -5 print (X, Y, X * Y, sep = '\n')

该函数仅适用于整数值 n, ,即使对于某些矩阵,我们也可以定义分数幂,例如平方根(换句话说,矩阵 B 这样 B * B = A).

  • 锻炼: 求平方根 diag(-1, -1). 。这是唯一可能的答案吗?找到一个矩阵的例子 有一个平方根。

奖金:复数

下面我就用一节的时间给大家介绍一下这个主题!由于这是一个复杂的主题,我很可能会失败,所以请提前原谅我。

首先,类似于我们的矩阵 zeroone, ,我们可以通过做任何实数来制作一个矩阵 diag(number, number). 。这种形式的矩阵可以相加、相减、相乘、求逆,其结果将模拟数字本身所发生的情况。因此,出于所有实际目的,人们可以说,例如, diag(5, 5) 5.

然而,Python 还不知道如何处理以下形式的表达式 A + 1 或者 5 * B 在哪里 AB 是矩阵。如果您有兴趣,您一定应该去做以下练习或查看我的实现(它使用了一个很酷的 Python 功能,称为 装饰者);否则,只需知道它已实施即可。

  • 大师练习: 更改 a 中的运算符 Matrix 类,以便在所有标准运算中,其中一个操作数是矩阵,另一个是数字,该数字会自动转换为 diag 矩阵。还添加比较以确保相等。

这是一个测试示例:

print( 3 * A - B / 2 + 5 )

现在这是第一个有趣的 复数:矩阵 J, ,在开始时引入并且等于 Matrix((0, 1, -1, 0)), ,有一个有趣的属性 J * J == -1 (尝试一下!)。这意味着 J 当然不是一个正常的数字,但是,正如我刚才所说,矩阵和数字很容易混合在一起。例如,

(1 + J) * (2 + J) == 2 + 2 * J + 1 * J + J * J = 1 + 3 * J

使用之前列出的规则。如果我们用 Python 测试这个会发生什么?

(1 + J) * (2 + J) == 1 + 3*J

应该高兴地说 True. 。另一个例子:

(3 + 4*J) / (1 - 2*J) == -1 + 2*J 

正如您可能已经猜到的那样,数学家并不称这些为“疯狂的数字”,但他们做了类似的事情 - 他们称其为以下形式的表达式 a + b*J 复数。因为这些仍然是我们的实例 Matrix 类,我们可以用它们做很多操作:加法、减法、乘法、除法、幂——都已经实现了!矩阵是不是很神奇?

我忽略了如何打印操作结果的问题 E = (1 + 2*J) * (1 + 3*J) 这样它看起来就像一个表达式 J 而不是一个 2x2 矩阵。如果您仔细检查它,您会发现您需要以格式打印该矩阵的左列 ... + ...J (还有一件好事:正是如此 E(x_axis)!)那些知道之间区别的人 str()repr() 应该看到,命名一个函数来生成如下形式的表达式是很自然的 repr().

  • 锻炼: 编写函数 Matrix.__repr__ 就可以做到这一点并尝试一些测试,比如 (1 + J) ** 3, ,首先在纸上计算结果,然后用 Python 尝试。

  • 数学问题: 决定因素是什么 a + b*J?如果你知道什么 绝对值 复数为:它们是如何连接的?的绝对值是多少 a?的 a*J?

3.矩阵:(R)进化

在这个三部曲的最后一部分中,我们将看到一切都是一个矩阵。我们将从一般开始 M x N 矩阵,并找出如何将向量视为 1 x N 矩阵以及为什么数字与对角矩阵相同。作为旁注,我们将探讨复数: 2 x 2 矩阵。

最后,我们将学习使用矩阵编写仿射和射影变换。

所以计划的课程是 [MNMatrix, NVector, Affine, Projective].

我想如果你能耐心听我讲到这里,你可能会对这部续集感兴趣,所以我想听听我是否应该继续这个(以及在哪里,因为我非常确定我已经超越了被认为是单个文档的合理长度)。

其他提示

麻省理工学院在 http://ocw.mit.edu上在线提供了大量数学课程资料。 / OcwWeb /数学/ 。一旦掌握了基础知识,他们也可以在线获得物理笔记。

这个麻省理工学院的文件是必须掌握转型基础知识的必备知识。

http ://stellar.mit.edu/S/course/6/fa08/6.837/courseMaterial/topics/topic2/lectureNotes/03_transform/03_transform.pdf

初学者最好的书籍之一是Carl Meyer的“Matrix Analysis and Applied Linear Algebra”。

您可以在线查看整本书籍(虽然它有版权水印): http://www.matrixanalysis.com/DownloadChapters.html

你可能想看看第5章。 326-332,其涵盖了三维计算机图形中的旋转

你可能想看看I-Hsiung Lin,Yixiong Lin(ISBN:9812560874)的 Geometric linear algebra 。本书专门针对您的需求(2维和3维矢量空间的线性变换),并采用几何方法处理完整,渐进的细节(每个维度300页)。我担心它不是免费的,但你应该可以在任何一个好的大学图书馆找到它。否则, Bookfinder 应该可以帮助您以相对适中的价格获得它。

Jim Hefferon的免费线性代数教科书非常好。与太多免费电子书不同,Jim显然已经花时间制作出优秀的读者和线性代数的介绍。它并没有过多地使用正式的数学写作,而这种写作往往过于密集,定理和证明很容易理解。它还包含许多线性代数的实际应用的非常好的例子 - 坐标转换只是一个例子。你无法击败价格,它还附带了可选的解决方案。

P.S。如果坐标变换是您的事情,那么在完成线性代数后,您可能会对微分几何感兴趣。

这些是我找到的信息。其中一些可能对您有价值:

理论:

(正在寻找 “矩阵” 谷歌图书为您提供了大量讲座,其中一些与转换直接相关 - 是第一个结果,但我鼓励您检查更多。)

我也鼓励(我不知道这个词是否正确,我正在学习英语)你,在其中一本书中寻找此类信息(虽然它们不是免费的,但你可以找到大部分内容) Google 图书上的旧书):

  1. 游戏编程宝石7
  2. 游戏编程宝石6
  3. 游戏编程宝石5
  4. 游戏编程宝石4
  5. 游戏编程宝石3
  6. 游戏编程宝石2
  7. 游戏编程宝石

每一个都有关于数学宝石的部分 - 并且那里有很多巧妙的技巧。这些书每一分钱都值钱。

还有 GPU 编程精华,所以您也可以尝试一下。

实践:

如果我能找到更多,我会在这里编辑和添加链接,但说实话 - 我在使用谷歌大约 10 分钟内找到了这些链接。世界上最受欢迎的浏览器存储有关所有内容的数据 - 是的,“所有内容”也意味着矩阵。

队友的欢呼声。

我认为你应该花几天时间做点积和交叉产品与3D矢量。然后学习trig和向量之间的关系。在那之后,矩阵对你来说会更有意义。

MIT-OCW的Gilbert Strang关于线性代数的课程。令人难以置信的男人难以置信的讲座;如果您对矩阵的理解完全来自编程源(如MATLAB),那么线性代数课程肯定会为您提供基于矩阵的疯狂事物。

http://www.ocw.cn /OcwWeb/Mathematics/18-06Spring-2005/VideoLectures/index.htm

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top