Lineare Algebra

Die lineare Algebra beschäftigt sich mit rechteckigen Strukturen von Zahlen und den Rechenregeln, nach denen diese addiert oder multipliziert werden. Dabei nennt man eine Struktur, in der Zahlen in m Zeilen und n Spalten angeordnet sind, eine m x n Matrix.

(defstruct matrix
  (rows columns elements)
  (and
    (= rows (length elements))
    (every? (lambda (row) (equal? columns (length row))) elements)))

Hier wird eine Matrix dargestellt durch eine Datenstruktur mit drei Komponenten: einer Zahl ("rows"), die der Listenlänge der dritten Komponente ("elements") entspricht und bei der die Listenelemente wiederum Listen sind, die alle eine Länge haben, die der zweiten Komponente ("columns") entspricht.

Die Zahl der Elemente in einer Matrix entspricht dem Produkt aus Zeilenanzahl und Spaltenanzahl.

(defmethod get-element-count ((this matrix))
  t
  (* (. this rows) (. this columns)))

Eine Matrix mit nur einer Zeile nennt man Zeilenvektor.

(defclass row-vector (matrix))

(defmethod initialize ((this row-vector) columns elements)
  (= columns (length elements))
  (initialize this 1 columns (list elements)))

Hat eine Matrix nur eine Spalte, handelt es sich um einen Spaltenvektor.

(defclass column-vector (matrix))

(defmethod initialize ((this column-vector) rows elements)
  (= rows (length elements))
  (initialize this rows 1 (map-with (curry (cons _ nil)) elements)))

Zwei Matrizen können addiert werden, wenn sie gleiche Anzahlen von Zeilen und gleiche Anzahlen von Spalten haben. Die Summe zweier Matrizen berechnet sich, indem man jeweils die Einträge an gleichen Positionen der beiden Matrizen addiert.

(defmethod add ((this matrix) (that matrix))
  (and
    (= (. this rows) (. that rows))
    (= (. this columns) (. that columns)))
  (new matrix
    (. this rows)
    (. this columns)
    (zip-with
      (curry (zip-with add _ _))
      (. this elements)
      (. that elements))))

Eine Matrix wird mit einem Skalar multipliziert, indem alle Einträge der Matrix mit dem Skalar multipliziert werden.

(defmethod scalar-multiplication (scalar (this matrix))
  t
  (new matrix
    (. this rows)
    (. this columns)
    (map-with
      (curry (map-with (curry (* scalar _)) _))
      (. this elements))))

Zwei Matrizen können multipliziert werden, wenn die Spaltenanzahl der linken mit der Zeilenanzahl der rechten Matrix übereinstimmt. Dabei wird jede Zeile der linken Matrix mit jeder Spalte der rechten Matrix zu jeweils einem Element in der Ergebnismatrix verrechnet, indem das erste (zweite, ...) Element der Zeile mit dem ersten (zweiten, ...) Element der Spalte multipliziert wird und man die Produkte summiert.

(defmethod times ((this matrix) (that matrix))
  (= (. this columns) (. that rows))
  (new matrix
    (. this rows)
    (. that columns)
    (rows-*-columns (. this elements) (. that elements))))

(defproc rows-*-columns (left right)
  (if
    (null? (first right))
    (map-with (constantly nil) left)
    (zip-with
      cons
      (map-with (curry (row-*-first-column _ right)) left)
      (rows-*-columns
        left
        (without-first-column right))))

(defproc row-*-first-column (row rows)
  (apply +
    (zip-with
      (lambda (element vector) (* element (first vector)))
      row
      rows)))

(defproc without-first-column (rows)
  (map-with rest rows))

Das Produkt eines 1 x n Zeilenvektors u mit einem n x 1 Spaltenvektor w ist eine 1 x 1 Matrix, die als Zahl interpretiert wird.

(defmethod inner-product ((this row-vector) (that column-vector))
  (= (. this columns) (. that rows))
  (first (first (. (* this that) elements))))

Transponieren bedeutet, das aus einem Spaltenvektor ein Zeilenvektor wird, oder umgekehrt.

(defmethod transpose ((this column-vector))
  t
  (new row-vector
    (. this rows)
    (map-with first (. this elements))))


linear-algebra.sheet.xml