Матрични трансформации; концепции и теория, има ли безплатни ресурси за практическо обучение? [затворено]

Напоследък се забавлявам да изобразявам диаграми и графики от координати и съм очарован от използването на матрици за трансформиране на координатни пространства.

Успях успешно да мащабирам и обърна двумерни координатни пространства, но сега апетитът ми се изостри. :)

Къде мога да отида за ясен, информативен, (безплатен) образователен материал за матрици, матрична математика, особено що се отнася до 2- и 3-измерното пространство?


person John Weldon    schedule 19.06.2009    source източник


Отговори (10)


Оригинален отговор: Не съм сигурен дали ще ви хареса как математическите курсове обикновено въвеждат матрици. Като програмист може да сте по-щастливи да вземете някоя прилична книга за 3D графики. Със сигурност трябва да има много конкретни 3x3 матрици. Освен това открийте тези, които ще ви научат на проективни трансформации (проективната геометрия е много красива област с нискоразмерна геометрия и лесен за програмиране).

Мини-курс по матрична математика с Python 3

Съдържание:

  1. Матрици [Vector, __add__, reflect_y, rotate, dilate, transform]
  2. Матрици: Претоварени [Matrix, __add__, __str__, __mul__, zero, det, inv, __pow__]
  3. Бонус: Комплексни числа
  4. Матрици: (R)еволюцията. Вече е в процес на създаване (има резюме в края)

Предговор: Въз основа на моя преподавателски опит смятам, че курсовете, посочени от други, са много добри курсове. Това означава, че ако целта ви е да разбирате матриците, както го правят математиците, тогава непременно трябва да получите целия курс. Но ако целите ви са по-скромни, ето моят опит за нещо, което е по-подходящо за вашите нужди (но все пак написано с цел да предадете много теоретични концепции, което донякъде противоречи на първоначалния ми съвет.)

Как да използвате:

  • Този пост е дълъг. Може да помислите да отпечатате това и да продължите бавно, като една част на ден.
  • Кодът е от съществено значение. Това е курс за програмисти. Упражненията също са от съществено значение.
  • Трябва да разгледате придружаващия код, който съдържа целия този код и много повече
  • Това са 2 на цената на 1 специално: можете също да научите 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()

Вижте, ако погледнете как изчислява тази функция, всъщност е доста тривиално. Но изведнъж се случи невероятно нещо: успях да напиша трансформация, използвайки само съществуващите трансформации flip и reflect_x. За всички ме интересува, reflect_y може да бъде дефиниран в производен клас без достъп до x и y и пак ще работи!

Математиците биха нарекли тези функции оператори. Те биха казали, че reflect_y е оператор, получен чрез състав на оператори flip и reflect_x, който се обозначава с reflect_y = flip ○ reflect_x (трябва да видите малкия кръг, символ на Unicode 25CB).

  • Забележка: Съвсем свободно ще използвам символа = отсега нататък, за да обознача, че две операции дават един и същ резултат, както в параграфа по-горе. Това е математическо =, което не може да бъде изразено като програма.

So if I do

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 разширява осите 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)

което би взело всеки 4-кортеж от числа, наречено матрица, и би го приложило към вектор x. Ето един пример:

rotation
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)
matrix = (0, -1, 1, 0) print(v, v.rotate_90(), v.transform(rotation
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)
matrix))

който отпечатва (5, 3) (-3, 5) (-3, 5). Обърнете внимание, че ако приложите transform с която и да е матрица към произход, пак ще получите произход:

origin = Vector(0, 0)
print(origin.transform(rotation
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)
matrix))
  • Упражнение: кои са кортежите m, които описват flip, dilate(x, y), rotate(angle)?

Докато се разделяме с класа Vector, ето едно упражнение за тези, които искат да тестват както знанията си по векторна математика, така и уменията си по Pythonic:

  • Последната битка: Добавете към класа Vector всички векторни операции, които можете да измислите (колко стандартни оператора можете да претоварите за вектори? Вижте отговора ми).
  1. Матрици: Претоварени =

Както разбрахме в предишния раздел, матрицата може да се разглежда като стенограма, която ни позволява да кодираме векторна операция по прост начин. Например rotation

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)
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) . Оказва се, че ако m1 и m2 са били кодирани от матрици, вие също можете да кодирате това m с помощта на матрици!

  • Упражнение: Помислете за всички трудности, които може да имате с това твърдение.

Просто трябва да добавите неговите данни, като (0, 1, -1, 0) + (0, 1, -1, 0) = (0, 2, -2, 0). Ето как да добавите два кортежа в Python с някои много полезни и силно 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)
  • Упражнение: Разгледайте кода за 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, направени от R и diag, така че 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))

Нека намерим аналози на обикновените числа 0 и 1 и изваждането:

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.

  • Упражнение: намерете A и b така, че завъртане с 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 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). Това ли е единственият възможен отговор? Намерете пример за матрица, която няма квадратен корен.

Бонус: Комплексни числа

Тук ще ви запозная с темата точно в един раздел! Тъй като темата е сложна, има вероятност да се проваля, така че моля да ме извините предварително.

Първо, подобно на това как имаме матрици zero и one, можем да направим матрица от всяко реално число, като направим diag(number, number). Матриците от тази форма могат да се добавят, изваждат, умножават, обръщат и резултатите ще имитират това, което се случва със самите числа. Така че за всички практически цели може да се каже, че например diag(5, 5) е 5.

Python обаче все още не знае как да обработва изрази от формата A + 1 или 5 * B, където A и B са матрици. Ако се интересувате, непременно трябва да отидете и да направите следното упражнение или да разгледате моята реализация (която използва страхотна функция на 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].

Предполагам, че ако успяхте да ме издържите дотук, може да се интересувате от това продължение, така че бих искал да чуя дали трябва да продължа с това (и къде, тъй като съм почти сигурен, че съм отвъд това, което счита се за разумна дължина на един документ).

person Community    schedule 20.06.2009
comment
Харесва ми този отговор, Иля! Намирам точно това, което предлагате, за вярно. Бих искал да се ориентирам в математиката, но не изглежда толкова практична, колкото ми се иска. Ще проверя 3D графики. - person John Weldon; 21.06.2009
comment
Имам почивка... ще напиша следващата половина по-късно. - person ilya n.; 28.06.2009
comment
Опитайте се да познаете какво е "The Revolution" :) - person ilya n.; 28.06.2009
comment
Разочароващ завършек ли е на иначе страхотна трилогия? ;) - person Alex Mcp; 29.06.2009
comment
Това е най-дългият отговор на Stack Overflow, с изключение на тези, които са направени основно от кодови блокове: meta.stackexchange.com/questions/42149/ - person Thomas Bonini; 13.03.2010
comment
Така трябва да се преподава линейна алгебра. Всеки ученик, който след това напредва към „разбиране на матриците, както правят математиците“, ще има фантастично интуитивно разбиране на концепциите. - person Alex; 18.06.2010
comment
Връзката към придружителя на кода, споменат в този отговор (web.mit.edu/~unknot /www/matrices.py) не работи. Моля, дайте работеща връзка, ако съществува - person hexicle; 19.06.2013

Масачузетският технологичен институт разполага с много от техните материали за курсове по математика онлайн на http://ocw.mit.edu/OcwWeb/Mathematics/. След като научите основите, те имат и бележките по физика онлайн.

person stevedbrown    schedule 19.06.2009
comment
+1, тъй като имам този курс от iTunes, много добър ресурс. - person ; 19.06.2009
comment
По-специално курсът по линейна алгебра, който има налични видео лекции. ocw.mit.edu/OcwWeb/Mathematics/18- 06Пролет-2005/Начало на курса/ - person Bill the Lizard; 19.06.2009
comment
Вторих курса по линейна алгебра на Гилбърт Странг и може би е добра идея да взема и неговия учебник. - person Michael Dorfman; 22.06.2009
comment
същото. книгата има голям фокус върху векторните пространства / базис. - person Jason S; 23.06.2009

това е http://en.wikipedia.org/wiki/Computer_graphics. две от ключовите концепции са http://mathworld.wolfram.com/LinearTransformation.html и http://mathworld.wolfram.com/AffineTransformation.html.

person Ray Tayek    schedule 19.06.2009

Този документ от Масачузетския технологичен институт е задължителен, за да получите сериозни познания по основите на трансформацията.

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

person Christophe Eblé    schedule 24.06.2009
comment
Благодаря ти! Всички материали от MIT изглеждат полезни, разбира се :) - person John Weldon; 27.06.2009

Една от най-добрите книги за начинаещи е „Матричен анализ и приложна линейна алгебра“ на Карл Майер.

Можете да видите цялата книга онлайн тук (въпреки че има воден знак за авторски права): http://www.matrixanalysis.com/DownloadChapters.html

Може да искате да погледнете глава 5, стр. 326 - 332, който обхваща ротациите в триизмерната компютърна графика

person codehippo    schedule 26.06.2009
comment
Благодаря ти! Ще проверя това. - person John Weldon; 27.06.2009

Може да искате да разгледате Геометрична линейна алгебра от I-Hsiung Lin, Yixiong Lin (ISBN: 9812560874). Книгата е специално насочена към това, което искате (линейни трансформации на 2- и 3-измерни векторни пространства) и го третира с геометричен подход в пълни, прогресивни детайли (300 страници за всяко измерение). Страхувам се, че не е безплатно за закупуване, но трябва да можете да го намерите във всяка добра университетска библиотека. В противен случай Bookfinder трябва да ви помогне да го получите на сравнително скромна цена.

person Francois G    schedule 26.06.2009
comment
хм, малко по-скъпо от безплатното :) но книгата изглежда добре! - person John Weldon; 27.06.2009

Безплатният Линейна алгебра учебник на Jim Hefferon е наистина добър. За разлика от твърде много безплатни електронни книги, Джим очевидно е отделил време, за да създаде отличен четец и въведение в линейната алгебра. Не е прекалено обременен с формално математическо писане, което често е твърде гъсто с теореми и доказателства, за да бъде лесно разбираемо. Той също така съдържа много наистина отлични примери за реални приложения на линейната алгебра - координатните трансформации са само един пример. Не можете да победите цената, а също така идва с незадължителни решения на упражненията.

P.S. Ако трансформациите на координатите са вашето нещо, може да се интересувате от диференциална геометрия, след като приключите с линейната алгебра.

person Jiahao Chen    schedule 26.06.2009
comment
Това изглежда много интересно, благодаря! Ще трябва да проверя и диференциалната геометрия :) - person John Weldon; 27.06.2009

Това е информацията, която намерих. Някои от тях може да са ценни за Вас:

теория:

(Търсене на "Матрици" в Google книги ви дава много лекции, някои от които са пряко свързани с трансформации - това е един от първите резултати, но ви радвам да проверите повече.)

Също така ви насърчавам (не знам дали това е правилната дума, просто уча английски) да потърсите този вид информация в някоя от тези книги (въпреки че не са безплатни, но можете да намерите големи части от по-стари в Google Книги):

  1. Скъпоценни камъни за програмиране на игри 7
  2. Скъпоценни камъни за програмиране на игри 6
  3. Скъпоценни камъни за програмиране на игри 5
  4. Скъпоценни камъни за програмиране на игри 4
  5. Скъпоценни камъни за програмиране на игри 3
  6. Скъпоценни камъни за програмиране на игри 2
  7. Скъпоценни камъни за програмиране на игри

Всеки от тях има раздел за математически скъпоценни камъни - и там има много кокетни трикове. Тези книги си струват всеки цент.

Има и скъпоценни камъни за програмиране на GPU, така че можете да опитате и тях.

практика:

Ако намеря още, ще редактирам и добавя връзки тук, но честно казано - намерих тези връзки за около 10 минути използване на Google. Най-популярният браузър в света съхранява данни за всичко - и да, "всичко" означава също и матрици.

Наздраве, Мате.

person zeroDivisible    schedule 26.06.2009
comment
благодаря за цялата тази агрегация! Има много храна за размисъл. - person John Weldon; 27.06.2009
comment
И да, правилно си използвал думата насърчаване :) - person John Weldon; 28.06.2009

Мисля, че трябва да прекарате няколко дни в правене на точкови произведения и кръстосани произведения с вектори в 3D. След това научете връзката между тригонометрия и вектори. След това матриците ще имат много повече смисъл за вас.

person Nosredna    schedule 26.06.2009
comment
Отлична идея! Направих много тригонометрични и 3D моделиране на моя 286 в GWBasic през деня :) Току-що разбрах, че някои от позициите в матрицата са синусови и косинусови стойности ... добри неща. - person John Weldon; 27.06.2009

Курсът на MIT-OCW по линейна алгебра от Гилбърт Странг. Невероятни лекции от невероятен човек; ако вашето разбиране за матриците е извлечено единствено от източници на програмиране (като MATLAB), тогава курсът по линейна алгебра определено ще ви даде основите да правите луди неща с матрици.

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

person Jacob    schedule 28.06.2009