Матричные преобразования; концепции и теория, есть ли бесплатные ресурсы для практического обучения?

В последнее время я с удовольствием отрисовываю диаграммы и графики по координатам, и меня восхищает использование матриц для преобразования координатных пространств.

Я смог успешно масштабировать и инвертировать двумерные координатные пространства, но теперь мой аппетит разжег. :)

Где я могу найти ясный, информативный (бесплатный) учебный материал по матрицам, матричной математике, особенно применительно к 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. Матрицы =

Векторы

Перед матрицами идут векторы. Вы точно знаете, как работать с двумерными и трехмерными векторами:

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 (вы должны увидеть маленький кружок, символ Юникода 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, вы все равно получите origin:

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, вот упражнение для тех, кто хочет проверить как свои знания векторной математики, так и навыки Python:

  • Последняя битва: добавьте в класс 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 с помощью некоторых очень полезных и высоко питонических приемов:

    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], второе на -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. Как насчет того, чтобы написать красивую функцию, использующую нотацию A ** n для A n? Обратите внимание, что наивный цикл 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 под названием decorator); в противном случае просто знайте, что это реализовано.

  • Упражнение для гуру: измените операторы в классе 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
Попробуйте угадать, что такое «Революция» :) - 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/MatMathematics/. Когда вы освоите основы, у них также появятся заметки по физике в Интернете.

person stevedbrown    schedule 19.06.2009
comment
+1 так как у меня этот курс из iTunes очень хороший ресурс. - person ; 19.06.2009
comment
В частности, курс линейной алгебры, в котором есть видеолекции. ocw.mit.edu/OcwWeb/Mat 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

Этот документ MIT необходим для получения глубоких знаний об основах трансформации.

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

Вы можете изучить Геометрическую линейную алгебру И-Сюн Линь, Иксюн Линь (ISBN: 9812560874). Книга специально ориентирована на то, что вы хотите (линейные преобразования 2-х и 3-х мерных векторных пространств), и рассматривает это с геометрическим подходом во всех деталях, прогрессивных деталях (300 страниц для каждого измерения). Боюсь, это не бесплатно, но вы сможете найти его в любой хорошей университетской библиотеке. В противном случае Bookfinder поможет вам получить его по относительно скромной цене.

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

Бесплатный учебник по линейной алгебре Джима Хефферона действительно хорош. В отличие от множества бесплатных электронных книг, Джим явно нашел время, чтобы создать отличного читателя и ввести в линейную алгебру. Он не обременен формальным математическим письмом, которое часто слишком насыщено теоремами и доказательствами, чтобы их можно было легко понять. Он также содержит множество действительно отличных примеров реальных приложений линейной алгебры - преобразования координат являются лишь одним из примеров. Вы не можете превзойти цену, и в ней также есть дополнительные решения для упражнений.

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
Отличная идея! В свое время я делал много триггерного и трехмерного моделирования на моем 286 в GWBasic :) Я только что понял, что некоторые позиции в матрице являются значениями синуса и косинуса ... хороший материал. - person John Weldon; 27.06.2009

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

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

person Jacob    schedule 28.06.2009