私は最近、座標からチャートやグラフをレンダリングするのを楽しんでおり、マトリックスを使用して座標空間を変換することに魅了されています。
2 次元座標空間のスケーリングと反転に成功しましたが、今では食欲がそそられます。:)
特に 2 次元および 3 次元空間に適用される、行列、行列数学に関する明確で有益な (無料の) 教材はどこで入手できますか?
私は最近、座標からチャートやグラフをレンダリングするのを楽しんでおり、マトリックスを使用して座標空間を変換することに魅了されています。
2 次元座標空間のスケーリングと反転に成功しましたが、今では食欲がそそられます。:)
特に 2 次元および 3 次元空間に適用される、行列、行列数学に関する明確で有益な (無料の) 教材はどこで入手できますか?
元の回答:数学の授業で一般的に行列がどのように導入されるかが気に入るかどうかわかりません。プログラマーとしては、まともな 3D グラフィックス ブックを手に入れたほうが幸せかもしれません。確かに、非常に具体的な 3x3 マトリックスが必要です。また、射影変換を教えてくれるものを見つけてください(射影幾何学は、低次元幾何学の非常に美しい領域であり、プログラミングも簡単です)。
コンテンツ:
[Vector, __add__, reflect_y, rotate, dilate, transform]
[Matrix, __add__, __str__, __mul__, zero, det, inv, __pow__]
序文:私の教育経験に基づいて、他の人が参照しているコースは非常に良いコースだと思います。つまり、数学者が行うように行列を理解することが目標である場合は、必ずコース全体を取得する必要があります。しかし、あなたの目標がより控えめである場合は、ここであなたのニーズにより合わせたものを試してみます (ただし、多くの理論的概念を伝えることを目的として書かれており、私の最初のアドバイスとは矛盾しています)。
使い方:
行列の前にベクトルがあります。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()
この関数の計算方法を見ると、実際には非常に簡単です。しかし、突然驚くべきことが起こりました。既存の変換flip
とreflect_x
. すべての場合、andreflect_y
にアクセスせずに派生クラスで定義でき、それでも機能します!x
y
数学者はこれらの関数をoperatorと呼びます。彼らは、それは演算子の合成reflect_y
によって得られた演算子であり、で示されると言うでしょう(小さな円、Unicode シンボルが表示されるはずです)。flip
reflect_x
reflect_y = flip ○ reflect_x
25CB
=
上の段落のように、2 つの操作が同じ結果を生成することを示すために、ここから記号を自由に使用します。これは、プログラムとして表現できない「数学的=
」です。だから私がするなら
print(v.reflect_y())
結果が得られ(-5, 3)
ます。行ってイメージしてください!
reflect_y ◦ reflect_y
。どのように名前を付けますか?これらの操作は素晴らしく便利でしたが、回転の導入がなぜこれほど遅いのか疑問に思われることでしょう。わかりました、ここに行きます:
def rotate(self:'vector', angle:'rotation angle') -> 'vector':
??????
この時点で、ベクトルを回転する方法を知っている場合は、続けて疑問符を埋めてください。それ以外の場合は、もう 1 つの単純なケースについてご容赦ください。反時計回りに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
拡張します。x
y
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)
これは、 matrixと呼ばれる数字の 4 タプルを取り、それを vectorに適用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 に任意の行列を適用しても、origin が取得されることに注意してください。
origin = Vector(0, 0)
print(origin.transform(rotation_90_matrix))
m
を記述するタプルは何ですか?flip
dilate(x, y)
rotate(angle)
クラスのVector
最後に、ベクトル数学の知識と Python のスキルの両方をテストしたい人のための演習があります。
Vector
ます (ベクトルに対していくつの標準演算子をオーバーロードできますか? 私の答えを確認してください)。前のセクションでわかったように、行列は、ベクトル演算を簡単な方法でエンコードできる省略形と考えることができます。たとえば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__
の意味をオーバーロードして、ベクトルに作用(...)
する行列の標準表記法を使用できるようにします。また、行列は通常、大文字 1 文字を使用して記述されます。
J = Matrix(rotation_90_matrix)
print(w, 'rotated is', J(w))
では、行列を使って他に何ができるか見てみましょう。m
行列は実際には、ベクトルに対する操作をエンコードする方法にすぎないことを思い出してください。2 つの関数の場合、新しい関数を作成できることに注意してください (m1(x)
ラムダ表記を使用) 。と が行列でエンコードされていた場合、行列を使用してこれをエンコードすることもできます。m2(x)
m = lambda x: m1(x) + m2(x)
m1
m2
m
のように、そのデータを追加するだけです(0, 1, -1, 0) + (0, 1, -1, 0) = (0, 2, -2, 0)
。Python で 2 つのタプルを追加する方法を次に示します。これには、非常に便利で非常に Pythonic な手法がいくつかあります。
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
になりましたが、結果を確認するには、Matrix を出力する方法を理解する必要があります。可能な方法は、数値の 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))
1 パラメータ関数でできる最も重要なことは、それらを構成することです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)
for も同様に、括弧を開くと、 というような新しいタプル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
決定した方法です。これは、表記法として理にかなっています:は operator を記述する行列であり、次に説明するように、これを「乗算」と呼ぶより深い理由もあります。A * B
A ○ B
Matrix
Python で乗算の使用を開始するには、クラスで次のように並べ替えるだけです。
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
から作られた行列 の例を挙げてください。は と等しくありません。R
diag
A * B
B * A
compose
数値の乗算は常に可換であるため、これはやや奇妙であり、本当に を呼び出すに値するかどうかという疑問が生じ__mul__
ます。+
以下は、および*
が満たす非常に多くのルールです。
A + B = B + A
A * (B + C) = A * B + A * C
(A + B) * C = A * C + B * C
(A * B) * C = A * (B * C)
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))
0
通常の数と1
引き算の類似物を見つけてみましょう。
zero = diag(0, 0)
one = diag(1, 1)
次の簡単に検証可能な追加を使用します。
A + zero = A
A * zero = zero
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
A
と を見つけます。彼らはユニークですか?b
pi/2
(1, 0)
すべてのベクトルに対して、ベクトルによるシフトであるアフィン変換があることに注意してください。
アフィン変換は形状を引き延ばしたり膨張させたりすることがありますが、どこでも同じように行う必要があります。ここで、任意の図形の面積が変換によって一定数だけ変化することを信じていただければ幸いです。行列によって与えられる変換の場合、A
この係数は の行列式と呼ばれ、面積の式を 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
です。
ご覧のとおり、回転行列の行列式は常に同じです。
from random import random
r = R(random())
print (r, 'det =', r.det())
興味深い点の 1 つdet
は、それが乗法であることです (瞑想を十分に長く行っていれば、定義から導かれるようなものです)。
A = Matrix((1, 2, -3, 0))
B = Matrix((4, 1, 1, 2))
print(A.det(), '*', B.det(), 'should be', (A * B).det())
行列を使ってできる便利なことは、2 つの線形方程式系を書くことです。
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]
、2 番目に -Am 1を掛け、 (疑わしい場合は、紙の上でこれを行います) を加えて を解き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で表すのはそのためです。nA ** n
の表記法を使う素敵な関数を書いてみませんか? 単純なサイクルは O(|n|) であり、これは確かに遅すぎることに注意してください。これは、次の複雑さで実行できるためです。A
for i in range(n): answer *= self
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)
ます。これが唯一の可能な答えですか?平方根を持たない行列の例を見つけてください。ここでは、このテーマを 1 つのセクションで紹介します。複雑な題材なので失敗する可能性が高いので、あらかじめご容赦ください。
まず、行列zero
との場合と同様にone
、 を実行することで任意の実数から行列を作成できますdiag(number, number)
。その形式の行列は、加算、減算、乗算、反転することができ、結果は数値自体で何が起こるかを模倣します。diag(5, 5)
したがって、すべての実用的な目的のために、たとえば 5 であると言えます。
A + 1
ただし、Python は、フォームまたは5 * B
whereA
およびB
are 行列の式を処理する方法をまだ知りません。興味がある場合は、次の演習を行うか、私の実装 ( decoratorと呼ばれるクールな Python 機能を使用) を参照してください。それ以外の場合は、実装されていることを知っておいてください。
Matrix
、オペランドの 1 つが行列で、もう 1 つが数値であるすべての標準演算で、数値が自動的に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)
式のように表示する方法の問題を見落としていました。注意深く調べると、その行列の左の列を次の形式で出力する必要があることがわかります(もう 1 つ良い点があります。まさにです!) と の違いを知っている人は、関数に名前を付けるのが自然だとわかるはずですのような形式の表現を生成します。J
2x2
... + ...J
E(x_axis)
str()
repr()
repr()
演習:まさにそれを行う関数を書き、Matrix.__repr__
いくつかのテストを試してみてください。たとえば(1 + J) ** 3
、最初に紙の上で結果を計算し、次に Python で試してみてください。
数学の問題:の行列式はa + b*J
何ですか? 複素数の絶対値が何であるかを知っている場合: それらはどのように接続されていますか? の絶対値はa
?のa*J
?
この三部作の最後の部分では、すべてがマトリックスであることがわかります。一般的な行列から始めてM x N
、ベクトルを行列と見なす方法1 x N
と、数値が対角行列と同じ理由を調べます。補足として、複素数を2 x 2
行列として調べます。
最後に、行列を使用してアフィン変換と射影変換を書く方法を学びます。
したがって、予定されているクラスは[MNMatrix, NVector, Affine, Projective]
.
ここまで我慢できれば続編にも興味を持っていただけるのではないかと思いますので、これを続けるべきかどうか(そしてどこで、私は何を超えていると確信しているので)を聞きたいと思います。 1 つのドキュメントの妥当な長さと見なされます)。
MIT には、オンラインのhttp://ocw.mit.edu/OcwWeb/Mathematics/で、多くの数学コースの教材があります。基礎を理解したら、物理ノートもオンラインで入手できます。
これはhttp://en.wikipedia.org/wiki/Computer_graphicsです。2 つの重要な概念は、 http ://mathworld.wolfram.com/LinearTransformation.htmlとhttp://mathworld.wolfram.com/AffineTransformation.htmlです。
初心者向けの最高の本の 1 つは、Carl Meyer の「Matrix Analysis and Applied Linear Algebra」です。
オンラインで本全体を表示できます (ただし、著作権の透かしがあります): http://www.matrixanalysis.com/DownloadChapters.html
第 5 章 pg を参照してください。326 - 332 は 3 次元コンピューター グラフィックスの回転をカバーします。
Gilbert Strang による線形代数に関する MIT-OCW のコース。信じられないほどの男による信じられないほどの講義。行列の理解がプログラミング ソース (MATLAB など) からのみ得られたものである場合、線形代数のコースは、行列でクレイジーなことを行うための基礎を確実に提供します。
http://www.ocw.cn/OcwWeb/Mathematics/18-06Spring-2005/VideoLectures/index.htm
3D でベクトルを使用して内積と外積を行うには、数日を費やす必要があると思います。次に、三角関数とベクトルの関係を学びます。その後、行列はあなたにとってより意味のあるものになります。
それらは私が見つけた情報です。それらのいくつかはあなたにとって価値があるかもしれません:
仮説:
( Google ブックスで「行列」を検索すると、多くの講義が得られます。そのうちのいくつかは、変換に直接関係しています。これは最初の結果の 1 つですが、もっと調べてみてください。)
私もお勧めします (これが正しい言葉かどうかはわかりません。英語を勉強しているだけです)。これらの本でこの種の情報を探すことをお勧めします (無料ではありませんが、大部分を見つけることができます)。 Google ブックスの古いもの):
それらのそれぞれには、数学の宝石に関するセクションがあり、そこにはたくさんの巧妙なトリックがあります. それらの本は 1 セントの価値があります。
GPU プログラミングの gem もありますので、そちらも試してみてください。
練習:
もっと見つけたら、ここにリンクを編集して追加しますが、正直なところ、Google を使用して約 10 分でこれらのリンクを見つけました。世界で最も人気のあるブラウザは、あらゆるものに関するデータを保存します。そうです、「すべて」とは行列も意味します。
歓声メイト。
I-Hsiung Lin, Yixiong Lin (ISBN : 9812560874) による幾何学的線形代数を参照することをお勧めします。この本は、あなたが望むもの (2 次元および 3 次元のベクトル空間の線形変換) に特化しており、幾何学的なアプローチで完全かつ漸進的な詳細 (各次元で 300 ページ) を扱います。申し訳ありませんが、無料で購入することはできませんが、優れた大学の図書館で見つけることができるはずです。それ以外の場合は、ブックファインダーを使用すると、比較的手頃な価格で入手できます.