矩阵变换;概念和理论,有没有免费的学习实践资源?

28

最近我一直在从坐标渲染图表和图形中获得乐趣,并且对使用矩阵来转换坐标空间非常着迷。

我已经成功地缩放和反转了二维坐标空间,但现在我的兴趣被激发了。

我想知道在哪里可以找到关于矩阵、矩阵数学的清晰、信息量大的(免费)教育材料,特别是与二维和三维空间相关的材料?

10个回答

49

原始答案:我不确定你是否会喜欢数学课程通常如何介绍矩阵。作为程序员,你可能更喜欢阅读任何一本不错的三维图形书籍。它肯定会有非常具体的3x3矩阵。此外,找出那些将教你projective transformations(投影几何是低维几何中非常美丽且易于编程的领域)的书籍。

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

目录:

  1. 矩阵 [向量,__add__,reflect_y,rotate,dilate,transform]
  2. 矩阵:重载 [矩阵,__add__,__str__,__mul__,zero,det,inv,__pow__]
  3. 额外奖励:复数
  4. 矩阵:(R)evolution。它已经在制作中了(结尾有摘要)

前言:基于我的教学经验,我认为其他人提到的课程都是非常好的课程。这意味着如果你的目标是像数学家一样理解矩阵,那么你应该全盘接受整个课程。但是,如果你的目标更为谦虚,那么这里是我尝试根据你的需求量身定制的内容(但仍旧旨在传达许多理论概念,有点与我的原始建议相矛盾)。

使用方法:

  • 这篇文章很长,你可以考虑打印出来,慢慢看,每天看一部分。
  • 代码是必不可少的。这是一个面向程序员的课程。练习也是必不可少的。
  • 你应该查看代码伴侣,其中包含所有这些代码和更多内容
  • 这是“1份价格2份货”的特价:您还可以在这里学习Python 3和复数。
  • 我非常重视任何阅读此文章的尝试(我是否正式有资格成为最长的文章?),因此如果您不理解某些内容(以及您理解了的内容),请随意评论。
  1. 矩阵 =

向量

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

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)

现在能够写出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)

哇塞,它真的很丑!

  • 注释: 我们在上面的例子中对x应用了操作rotate(90)。我们得到的知识是rotate(90) != rotate_90

  • 问题:这里发生了什么?如何用rotate来表示rotate_90?如何用rotate来表示flip

扩张

这些旋转肯定很有用,但它们并不是你需要做的所有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 函数来演示数学家所称的“交换律”:也就是说,对于参数 abcd 的每个值,您都可以确信

dilate(a, b) ◦ dilate(c, d) = dilate(c, d) ◦ dilate(a, b)
  • Exercise: Prove it. Also, is it true that for all possible values of parameters those below would hold?

    •  `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操作符

  • flipreflect_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)

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

  • 示例: 计算__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)

该程序将接收任何四元组数字,称为矩阵,并将其应用于向量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))
  • 练习: 描述flipdilate(x, y)rotate(angle)的元组m是什么?

当我们与Vector类分别时,这里有一个练习,供那些想要测试他们的向量数学知识和Python技能的人使用:

  • 最后的战斗:Vector类添加所有你能想到的向量操作(你可以重载多少个标准运算符来处理向量?查看我的答案)。
  1. 矩阵:重载=

正如我们在前一节中发现的那样,矩阵可以被视为一种简便的方式,它允许我们以简单的方式编码向量操作。例如,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),我可以使用lambda notation创建一个新函数m = lambda x: m1(x) + m2(x)。事实证明,如果m1m2由矩阵编码,您也可以使用矩阵来编码这个m

  • 练习:思考一下您可能遇到的任何困难。

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

    def __add__(self:'matrix', snd:'another matrix'):
        """This will add two matrix arguments.
        
        snd is a standard notation for the 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)
  • Exercise: Examine the code for Vector.rotate(self, angle) and fill in the question marks. Test with

      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,如果你打开括号,看起来非常类似于使用新元组mMatrix.__call__,使得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)
  • Exercise: Fill in the question marks here. Test it with

      print(R(1).compose(R(2)))
      print(R(3))
    
  • Math exercise: Prove that R(a).compose(R(b)) is always the same as 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))查看为什么它被称为对角线矩阵。

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

  • 练习:如果需要,请返回并刷新可交换性问题。然后给出由Rdiag制成的矩阵AB的示例, 使得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
  • 练习: 证明这些陈述。如何用+*diag定义A - BA - A等于什么?在类Matrix中添加方法__sub__。如果计算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,使得围绕点(1,0)旋转pi/2具有上述形式。它们唯一吗?

请注意,对于每个向量,都存在一个仿射变换,它是向量的平移

仿射变换可以拉伸或扩大形状,但必须在任何地方以相同的方式进行。现在我希望您相信,在变换下,任何图形的面积都会按照一个常数改变。对于由矩阵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,这表明你总是可以通过将b乘以另一个矩阵来获得v。这个矩阵被称为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_axisA如上所述)。

逆矩阵的主要属性是A * A.inv()始终等于one

  • 练习: 检查一下。从逆矩阵的定义出发,解释为什么应该这样做。

这就是为什么数学家用A-1来表示A.inv()。我们可以写一个很好的函数,使用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 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 + 15 * B 的表达式,其中 AB 是矩阵。如果您感兴趣,可以尝试进行以下练习或查看我的实现(它使用了一个很酷的 Python 特性称为“装饰器”);否则,只需知道它已被实现即可。

  • 高手练习:更改 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的绝对值是多少?

  1. 矩阵:(R)演变

在这个三部曲的最后一部分中,我们将看到一切都是矩阵。我们将从一般的M x N矩阵开始,发现向量可以被视为1 x N矩阵,以及为什么数字与对角线矩阵相同。顺便说一句,我们将探讨复数作为2 x 2矩阵的方式。

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

因此,计划的类是[MNMatrix,NVector,Affine,Projective]

我猜如果你能一直忍受到这里,你可能会对这个续集感兴趣,所以我想听听是否应该继续这个话题(以及在哪里,因为我相当确定我已经超出了单个文档的合理长度)。


3
我正在休息中,稍后会继续写下一部分。 - ilya n.
这是一个本来很棒的三部曲却以令人失望的结局结束了吗? ;) - Alex Mcp
5
这是 Stack Overflow 上最长的答案,不包括主要由代码块组成的答案:http://meta.stackexchange.com/questions/42149/what-is-the-longest-single-answer-youve-ever-seen-on-so - Andreas Bonini
1
这就是线性代数应该教授的方式。任何学生如果能够进一步理解矩阵,就像数学家那样,将会对这些概念有一个极好的直观理解。 - Alex
此答案提到的代码伴侣链接(http://web.mit.edu/~unknot/www/matrices.py)无法使用。如果有可用的工作链接,请提供。 - hexicle
显示剩余2条评论

9

麻省理工学院有许多数学课程的材料可以在网上找到,网址为http://ocw.mit.edu/OcwWeb/Mathematics/。一旦你掌握了基础知识,他们也提供物理笔记在线查阅。


+1 因为我从iTunes上学习了这门课程,它是一个非常好的资源。 - littlegeek
4
特别是线性代数课程,其中有视频讲座可用。http://ocw.mit.edu/OcwWeb/Mathematics/18-06Spring-2005/CourseHome/index.htm - Bill the Lizard
2
我也推荐 Gilbert Strang 的线性代数课程,而且最好也买一本他的教材。 - Michael Dorfman
同上。这本书非常注重向量空间/基础的内容。 - Jason S

7

4

谢谢!MIT的材料当然都很有用 :) - John Weldon

3

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

你可以在这里在线查看整本书(尽管它带有版权水印): http://www.matrixanalysis.com/DownloadChapters.html

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


2
你可以参考《几何线性代数》(I-Hsiung Lin, Yixiong Lin 著,ISBN:9812560874)。这本书专门介绍了你所需要的内容(二维和三维向量空间的线性变换),并采用了几何学方法进行全面、详细的讲解(每个维度有300页)。很遗憾,这本书需要购买,但你应该能在任何好的大学图书馆找到它。否则,你可以在Bookfinder上以相对较低的价格购买到它。

嗯,比免费贵了一点 :) 但这本书看起来不错! - John Weldon

2
Jim Hefferon的免费《线性代数》教材非常好。与太多免费电子书不同,Jim花了大量时间制作出一本优秀的读者和线性代数入门教材。它没有过多的形式化数学写作,通常这些写作过于密集且定理和证明难以轻松理解。它还包含许多真实世界中线性代数应用的优秀示例,坐标变换只是其中之一。价格无法打败,并且还附带有可选的练习解答。

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


看起来非常有趣,谢谢!我也得去了解一下微分几何 :) - John Weldon

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

在 Google 图书 中搜索 "矩阵",会得到很多讲座,其中一些直接与变换相关 - this 是最早的结果之一,但我鼓励您查看更多。

我也鼓励(我不知道这是否是正确的词,我只是在学习英语)你,在这些书籍中寻找此类信息(虽然它们并不是免费的,但你可以在 Google 图书中找到很多旧版本的内容):

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

每一本书都有关于数学宝石的章节,里面有很多巧妙的技巧。这些书值得每一分钱。

还有GPU编程宝石,您也可以尝试一下。

练习:

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

干杯,伙计。


感谢您的所有聚合!那里有很多值得思考的内容。 - John Weldon
是的,你正确地使用了“鼓励”这个词 :) - John Weldon

2

我认为你应该花几天时间学习在三维空间中向量的点积和叉积。然后学习三角函数和向量之间的关系。这样,矩阵就会更容易理解了。


太棒了!我曾经在286的GWBasic上进行了很多三角函数和3D建模 :) 我刚刚意识到矩阵中的某些位置是正弦和余弦值...真是好东西。 - John Weldon

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接