Lineare Algebra
Colin Fahey
1. Software
LinearAlgebra.zip
Lineare Algebra Quellcode (C#)
19910 bytes
MD5: 11d8c8035cac30ba543e5e0b72ee9767
2. Einleitung
Dieser Artikel beschreibt, Vektoren und Matrizen in (d)-dimensionalen Raum.
3. (d)-dimensionalen Raum: Attribute
3.1 Array
„Array:“ Eine Sammlung von Variablen, so dass jede Variable hat einen eindeutigen Namen, so dass die Namen zugeordnet werden können eine Bestellung aufgeben.
Integer-Werte können verwendet werden, als Namen für die Variablen in einem Array.
Zum Beispiel, wenn ein Array enthält (d) Variablen, dann wird der Integer-Werte { 0, 1, 2, ..., (d-1) } können die Namen für die Variablen in das Array.
3.2 (d)-dimensionaler Vektor
„(d)-dimensionalen Vektors:“ Ein Array von (d) Variablen.
„Vektor-Komponente:“ Eine Variable innerhalb eines Vektors.
3.3 (d)-dimensionalen Vektorraum
„ein-dimensionalen Raum:“ Die komplette Reihe von Werten, die gespeichert werden können durch eine Variable.
„(d)-dimensionalen Raum:“ Die komplette Reihe von Kombinationen von Werten, die gespeichert werden können durch eine Reihe von Variablen (d).
Die formale Definition eines „Vektorraum:“
Lassen Sie (T) eine grundlegende Art (Beispiele: reelle Zahl, ganze Zahl, komplexe Zahl, rationale Zahl, etc.).
Jede Variable aus einem Grundbetrag Art wird eine „skalare.“
Ein „(T)-Typ (d)-dimensionalen Vektorraum“ ist die Menge (S) von (d)-dimensionalen Vektoren mit zwei Operationen, Vektor Neben (+) und skalare Multiplikation (*), Erfüllung der Bedingungen unter:
(1) Wenn (v) und (w) sind zwei beliebige Vektoren in (S), dann (v + w) ist auch ein Vektor in (S);
(2) Wenn (u), (v) und (w) sind alle drei Vektoren in (S), dann (u + v) + w = u + (v + w);
[Zusatzstoff commutativity]
(3) Wenn (v) und (w) sind zwei beliebige Vektoren in (S), dann (v + w) = (w + v);
[Zusatzstoff Assoziativität]
(4) Es ist ein „Null-Vektor,“ (0) in (S), so dass für jeden Vektor (v) in (S), (v + (0)) = v;
[Zusatzstoff Identität]
(5) Wenn (c) ist jede Art von skalaren (T) und (v) ist jeder Vektor in (S), dann das Produkt (c * v) ist ein Vektor in (S);
(6) Wenn (a), (b) und (c) sind alle Skalare vom Typ (T) und (v) und (w) sind alle Vektoren in (S), dann (a + b) * v = a*v + b*v und c*(v + w) = c*v + c*w;
[multiplikative distributivity]
(7) Wenn (a) und (b) sind alle Skalare vom Typ (T) und (v) ist jeder Vektor in (S), dann (a*b)*v = a*(b*v);
(8) Wenn „1“ ist ein Skalar vom Typ (T) so dass (1*1)=1 und (v) ist jeder Vektor in (S), dann (1*v) = v;
(9) Für jeden Vektor (v) in (S), der Vektor (-1)*v = -v erfüllt v + (-v) = (0);
[Zusatzstoff inverse]
3.4 (d)-dimensionale Vektor-Code
Der Code unten zeigt, wie ein (d)-dimensionaler Vektor, mit 64-Bit-Fließkomma-Komponenten umgesetzt werden können.
Ein Array ist ausreichend, um einen Vektor.
Der Code unten ist ein Array in einer Klasse, nur für Bequemlichkeit.
Der Code soll nicht effizient.
Eine Struktur (Beispiel: „struct“, einen Wert Typ) vertreten Vektoren einer festen Anzahl von Dimensionen (Beispiele: 3 oder 4) ist wahrscheinlich zu viel effizienter als die allgemeine (d)-dimensionale Vektor-Klasse hier gezeigt.
Obwohl Sie den untenstehenden Code definiert einen Vektor mit Fließkomma-Komponenten, dieses Dokument bedient sich auch Vektoren mit ganzzahligen Komponenten.
Der Code unten kann leicht modifiziert werden zur Umsetzung Vektoren mit ganzzahligen Komponenten.
using System;
using System.Collections.Generic;
using System.Text;
// Multidimensional vector with 64-bit floating-point components:
public class VectorF64
{
private double[] components;
public int Dimensions()
{
if (null == this.components)
{
return (0);
}
return (this.components.Length);
}
public VectorF64()
{
this.components = null;
}
public VectorF64( params double[] paramValues )
{
this.components = null;
if (null == paramValues)
{
return;
}
int dimensions = paramValues.Length;
this.components = new double[dimensions];
for (int i = 0; i < dimensions; i++)
{
this.components[i] = paramValues[i];
}
}
public VectorF64( VectorF64 other )
{
this.components = null;
if (null == other)
{
return;
}
if (null == other.components)
{
return;
}
int dimensions = other.Dimensions();
this.components = new double[dimensions];
for (int i = 0; i < dimensions; i++)
{
this.components[i] = other.components[i];
}
}
public double this[int index]
{
get
{
if (null == this.components)
{
return (0.0);
}
if
(
(index >= 0)
&& (index < this.components.Length)
)
{
return (this.components[index]);
}
return (0.0);
}
set
{
if (null == this.components)
{
return;
}
if
(
(index >= 0)
&& (index < this.components.Length)
)
{
this.components[index] = value;
}
}
}
public void Write( int precision )
{
if (null == this.components)
{
Log.Write( String.Empty + '(' + ' ' + ')' );
return;
}
int dimensions = this.Dimensions();
if (0 == dimensions)
{
Log.Write( String.Empty + '(' + ' ' + ')' );
return;
}
// Determine the largest component width in characters
// so that we can make all components an equal width.
int largestComponentWidth = 1;
for (int i = 0; i < dimensions; i++)
{
// { index [,minwidth] [:typeCode[precision]] }
// (minwidth<0) means left-justify
String text = String.Format( String.Empty + '{' + '0' + ':'
+ 'g' + precision + '}', this[i] );
if (text.Length > largestComponentWidth)
{
largestComponentWidth = text.Length;
}
}
Log.Write( '(' );
for (int i = 0; i < dimensions; i++)
{
Log.Write( ' ' );
String text =
String.Format( String.Empty + '{' + '0' + ','
+ largestComponentWidth + ':' + 'g' + precision + '}',
this[i] );
Log.Write( text );
if ((i + 1) < dimensions)
{
Log.Write( ',' );
}
else
{
Log.Write( ' ' );
}
}
Log.Write( ')' );
}
public void WriteLine( int precision )
{
this.Write( precision );
Log.WriteLine();
}
public void WriteLine()
{
const int defaultPrecision = 8;
this.Write( defaultPrecision );
Log.WriteLine();
}
// . . .
public static void Test()
{
// A 3-dimensional vector with 64-bit floating-point components:
VectorF64 v3 = new VectorF64( 0.0, 1.0, 2.0 );
v3.WriteLine(); // ( 0, 1, 2 )
// A 4-dimensional vector with 64-bit floating-point components:
VectorF64 v4 = new VectorF64( 0.0, 1.0, 2.0, 3.0 );
v4.WriteLine(); // ( 0, 1, 2, 3 )
// . . .
}
}
„(d) Null-dimensionaler Vektor:“ Ein Vektor mit allen Komponenten (d) gleich Null.
public class VectorF64
{
// . . .
public static VectorF64 Zero( int dimensions )
{
VectorF64 zero = new VectorF64();
zero.components = new double[dimensions];
for (int i = 0; i < dimensions; i++)
{
zero[i] = 0.0;
}
return (zero);
}
// . . .
public static void Test()
{
// . . .
// An 8-dimensional vector with all 8 64-bit floating-point
// components set to zero:
VectorF64 z = VectorF64.Zero( 8 );
z.WriteLine(); // ( 0, 0, 0, 0, 0, 0, 0, 0 )
// . . .
}
}
3.5 (d)-dimensionalen Raum: Punkte
„(d)-dimensionalen Raum Punkt:“ Ein Array von (d) Variablen mit bestimmten Werten „(koordinieren Werte).“
„(d)-dimensionalen Raum Herkunft:“ Ein Array von Variablen mit (d) alle Werte gleich Null.
3.6 (d)-dimensionale Vektoren: nicht-relativen und relativen
Ein (d)-dimensionaler Vektor ist, dass „nicht-relativen“ ist ein (d)-dimensionalen Vektor, direkt repräsentiert einen Status oder Konfiguration.
Ein (d)-dimensionaler Vektor, ist „relativ“ ist eine (d)-dimensionaler Vektor ist, dass Änderungen an einer Reihe von Komponenten.
Ein relativer Vektor darstellen können den Unterschied zwischen zwei nicht-relativen Vektoren.
Angesichts einer relativen Vektor, der Entscheidung, ob eine nicht-relativen Status oder Konfiguration über das relative Vektor erfordert, dass die Kombination von relativer Vektor mit einer nicht-relativen Vektor.
Nicht-relative Vektoren und relative Vektoren sind beide Vektoren.
Ob ein bestimmtes Vektor ist nicht-relativen oder relative muss angegeben werden, wenn der Vektor definiert ist.
Wenn ein (d)-dimensionaler Vektor ist interpretiert werden, dass Nicht-Verwandten, dann die (d)-dimensionaler Vektor darstellen können einen Punkt im (d)-dimensionalen Raum.
3.7 (d)-dimensionaler Vektor Addition, Subtraktion, und die Skalierung
Vector Addition, Subtraktion, und die Skalierung:
public class VectorF64
{
// . . .
public static VectorF64 operator +( VectorF64 a, VectorF64 b )
{
if ((null == a) || (null == b))
{
return (new VectorF64()); // Vector not specified.
}
if ((null == a.components) || (null == b.components))
{
return (new VectorF64()); // Vector is empty.
}
if (a.Dimensions() != b.Dimensions())
{
return (new VectorF64()); // Vectors not the same size.
}
int dimensions = a.Dimensions();
VectorF64 result = VectorF64.Zero( dimensions );
for (int i = 0; i < dimensions; i++)
{
result[i] = a[i] + b[i];
}
return (result);
}
public static VectorF64 operator -( VectorF64 a, VectorF64 b )
{
if ((null == a) || (null == b))
{
return (new VectorF64()); // Vector not specified.
}
if ((null == a.components) || (null == b.components))
{
return (new VectorF64()); // Vector is empty.
}
if (a.Dimensions() != b.Dimensions())
{
return (new VectorF64()); // Vectors not the same size.
}
int dimensions = a.Dimensions();
VectorF64 result = VectorF64.Zero( dimensions );
for (int i = 0; i < dimensions; i++)
{
result[i] = a[i] - b[i];
}
return (result);
}
public static VectorF64 operator -( VectorF64 a )
{
if (null == a)
{
return (new VectorF64()); // Vector not specified.
}
if (null == a.components)
{
return (new VectorF64()); // Vector is empty.
}
int dimensions = a.Dimensions();
VectorF64 result = VectorF64.Zero( dimensions );
for (int i = 0; i < dimensions; i++)
{
result[i] = (-( a[i] ));
}
return (result);
}
public static VectorF64 operator *( double scale, VectorF64 a )
{
if (null == a)
{
return (new VectorF64()); // Vector not specified.
}
if (null == a.components)
{
return (new VectorF64()); // Vector is empty.
}
int dimensions = a.Dimensions();
VectorF64 result = VectorF64.Zero( dimensions );
for (int i = 0; i < dimensions; i++)
{
result[i] = scale * a[i];
}
return (result);
}
public static VectorF64 operator *( VectorF64 a, double scale )
{
if (null == a)
{
return (new VectorF64()); // Vector not specified.
}
if (null == a.components)
{
return (new VectorF64()); // Vector is empty.
}
int dimensions = a.Dimensions();
VectorF64 result = VectorF64.Zero( dimensions );
for (int i = 0; i < dimensions; i++)
{
result[i] = scale * a[i];
}
return (result);
}
// . . .
public static void Test()
{
// . . .
// Examples of vector addition, subtraction, and scaling:
VectorF64 a = new VectorF64( 0.0, 1.0, 2.0, 3.0 );
a.WriteLine(); // ( 0, 1, 2, 3 )
VectorF64 b = new VectorF64( 3.0, 2.0, 1.0, 0.0 );
b.WriteLine(); // ( 3, 2, 1, 0 )
VectorF64 c = new VectorF64();
c.WriteLine(); // ( )
c = a + b;
c.WriteLine(); // ( 3, 3, 3, 3 )
c = a - b;
c.WriteLine(); // ( -3, -1, 1, 3 )
c = -b;
c.WriteLine(); // ( -3, -2, -1, 0 )
c = 3.0 * a;
c.WriteLine(); // ( 0, 3, 6, 9 )
// . . .
}
}
3.8 (d)-dimensionale Basis Vektoren
Die formale Definition des Begriffs der „Grundlage“ eines Vektorraum:
Lassen Sie (T) eine grundlegende Art (Beispiele: reelle Zahl, ganze Zahl, komplexe Zahl, rationale Zahl, etc.).
Jede Variable aus einem Grundbetrag Art wird eine „skalare.“
Lassen Sie (V) ein „(T)-Typ (d)-dimensionalen Vektorraum.“
Wenn die Nicht-Null-Vektoren { u1, u2, ..., ud } in (V) so sind, dass jeder Vektor (v) in (V) geschrieben werden können als eine „lineare Kombination“ dieser Vektoren, v = c1*u1 + c2*u2 + ... + cd*ud, wo { c1, c2, ..., cd } sind Skalare vom Typ (T), dann ist (V) „spanned“ von Vektoren { u1, u2, ..., ud }.
Jeder Satz von Null verschiedenen Vektoren { u1, u2, ..., ud } dass „span“ Vektorraum (V) trägt den Namen der „Grundlage“ von (V).
Eine einfache „Grundlage“ eines (d)-dimensionalen Vektorraum ist eine Menge von (d) unterschiedliche (d)-dimensionale Vektoren, die jeweils mit einem Teilbetrag in Höhe von ein und alle anderen Komponenten gleich Null.
Eine solche Grundlage „orthonormale“ Vektoren sind, was bedeutet, dass sie sich gegenseitig-im rechten Winkel „(orthogonal)“ und dass jeder Vektor hat Längeneinheit.
Jeder solche Vektor ist ein Einheitsvektor parallel zu einer der (d) Koordinatenachsen.
Ausdruck einer beliebigen Vektor als „Linearkombination“ von Vektoren dieser Basis ist direkt, jede einzelne Komponente der willkürlichen Vektor ist, multipliziert mit der entsprechenden Basis-Vektor, und diese Produkte werden addiert, um die willkürliche Vektor.
Der Code unten zeigt, wie ein Vektor ausgedrückt werden kann als „Linearkombination“ der Basis-Vektoren.
Der Code unten willkürlich definiert eine Reihe von orthonormale Basis Vektoren.
public class VectorF64
{
// . . .
public static VectorF64 BasisVector( int dimensions, int componentIndex )
{
if (dimensions < 0)
{
// Invalid number of dimensions specified.
return (new VectorF64());
}
VectorF64 basisVector = VectorF64.Zero( dimensions );
if ((componentIndex >= 0) && (componentIndex < dimensions))
{
basisVector[ componentIndex ] = 1.0;
}
return (basisVector);
}
// . . .
public static void Test()
{
// . . .
// The 4 basis vectors of 4-dimensional space,
// each with 4 64-bit floating-point components:
VectorF64 b0 = VectorF64.BasisVector( 4, 0 );
b0.WriteLine(); // ( 1, 0, 0, 0 )
VectorF64 b1 = VectorF64.BasisVector( 4, 1 );
b1.WriteLine(); // ( 0, 1, 0, 0 )
VectorF64 b2 = VectorF64.BasisVector( 4, 2 );
b2.WriteLine(); // ( 0, 0, 1, 0 )
VectorF64 b3 = VectorF64.BasisVector( 4, 3 );
b3.WriteLine(); // ( 0, 0, 0, 1 )
// . . .
}
}
Jeder Vektor in (d)-dimensionalen Raum kann ausgedrückt werden als Summe der Produkte aus Zahlen und Basis Vektoren:
public class VectorF64
{
// . . .
public static void Test()
{
// . . .
// The following two vectors are equivalent:
// A 4-dimensional vector with 64-bit floating-point components:
VectorF64 va = new VectorF64( 0.1, 1.1, 2.2, 3.3 );
va.WriteLine(); // ( 0.1, 1.1, 2.2, 3.3 )
// A 4-dimensional vector formed by scaling the 4 independent
// basis vectors for 4-dimensional space:
VectorF64 vb =
0.1 * VectorF64.BasisVector( 4, 0 )
+ 1.1 * VectorF64.BasisVector( 4, 1 )
+ 2.2 * VectorF64.BasisVector( 4, 2 )
+ 3.3 * VectorF64.BasisVector( 4, 3 );
vb.WriteLine(); // ( 0.1, 1.1, 2.2, 3.3 )
// . . .
}
}
3.9 (d)-dimensionalen Raum: Abstand zwischen den Punkten
Lassen Sie (P) ein (d)-dimensionaler Vektor, stellt einen Punkt in (d)-dimensionalen Raum.
Lassen Sie (Q) ein (d)-dimensionaler Vektor, stellt einen Punkt in (d)-dimensionalen Raum.
Lassen Sie (R) ein (d)-dimensionaler Vektor, entspricht der Veränderung (d) Koordinaten zu bekommen (P) von Punkt zu Punkt (Q); R = (Q - P).
In einem-dimensionalen Raum, P = ( p0 ), Q = ( q0 ) und R = (Q - P) = ( q0 - p0 ).
Der Abstand zwischen den beiden Punkten ist: Abs( q0 - p0 ).
In zwei-dimensionalen Raum, P = ( p0, p1 ), Q = ( q0, q1 ) und R = (Q - P) = ( q0-p0, q1-p1 ).
Interpretieren der zwei senkrecht Vertreibungen als die senkrecht Seiten eines Rechts-Dreieck, der Abstand zwischen den Punkten entspricht die Länge der Hypothenuse dieses Dreiecks.
Die Formel Pythagorean, (a*a) + (b*b) = (c*c), wo (a) und (b) sind die Längen der senkrechten Seiten eines Rechts-Dreieck, und (c) ist die Länge der Hypotenuse (Skew auf dieser Seite), kann verwendet werden, um festzustellen, der Abstand zwischen den zwei Punkten: Sqrt( Sq(q0-p0) + Sq(q1-p1) ).
In drei-dimensionalen Raum, P = ( p0, p1, p2 ), Q = ( q0, q1, q2 ) und R = (Q - P) = ( q0-p0, q1-p1, q2-p2 ).
Interpretieren der Vertreibung ( q0-p0, q1-p1, 0 ) als die senkrecht Seiten eines Rechts-Dreieck, und mit Hilfe der Formel Pythagorean, gibt die Entfernung zwischen Punkt (P) und dem Punkt ( q0, q1, p2 ): d01 = Sqrt( Sq(q0-p0) + Sq(q1-p1) ).
Die Verschiebung ( q0-p0, q1-p1, 0 ) ist senkrecht zur Vertreibung ( 0, 0, q2-p2 ), und eines anderen Rechts-Dreieck gebildet werden kann, und die Formel Pythagorean können wieder benutzt werden.
Somit ist die Entfernung von (P) Punkt-zu-Punkt (Q) ergibt sich aus: Sqrt( Sq(d01) + Sq(q2-p2) ) = Sqrt( (Sq(q0-p0) + Sq(q1-p1)) + Sq(q2-p2) ) = Sqrt( Sq(q0-p0) + Sq(q1-p1) + Sq(q2-p2) ).
Die Methode der Ausweitung der Formel Abstand von zwei-dimensionalen Raum zu drei-dimensionalen Raum angewendet werden kann wiederholt, um schließlich festzustellen, die Distanz zu (d) Formel-dimensionalen Raum: Sqrt( (Sq(q0-p0) + Sq(q1-p1)) + Sq(q2-p2) + ... + Sq(qd-pd) ).
Der Code unten definiert eine Funktion mit dem Namen der „Länge“ berechnet die Länge einer (d)-dimensionaler Vektor.
Wenn ein Vektor stellt die Verschiebung zwischen zwei Punkten in (d)-dimensionalen Raum, der Länge des Vektors ist der Abstand zwischen diesen beiden Punkten.
public class VectorF64
{
// . . .
public double Length()
{
if (null == this.components)
{
return (0.0); // Vector empty.
}
int dimensions = this.Dimensions();
double sumOfSquares = 0.0;
for (int i = 0; i < dimensions; i++)
{
sumOfSquares += (this[i] * this[i]);
}
double length = Math.Sqrt( sumOfSquares );
return (length);
}
public static double Length( VectorF64 a )
{
if (null == a)
{
return (0.0); // Vector not specified.
}
if (null == a.components)
{
return (0.0); // Vector is empty.
}
return (a.Length());
}
// . . .
public static void Test()
{
// . . .
// Example of vector length:
// A 6-dimensional vector representing a point (p):
VectorF64 p = new VectorF64( 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 );
p.WriteLine(); // ( 0, 1, 2, 3, 4, 5 )
// A 6-dimensional vector representing a point (q):
VectorF64 q = new VectorF64( -5.0, 4.0, -3.0, 2.0, -1.0, 0.0 );
q.WriteLine(); // ( -5, 4, -3, 2, -1, 0 )
// A 6-dimensional vector representing the displacement
// from point (p) to point (q):
VectorF64 r = q - p;
r.WriteLine(); // ( -5, 3, -5, -1, -5, -5 )
// The distance between point (p) and point (q)
// in 6-dimensional space:
double distance = r.Length();
Log.WriteLine( distance ); // 10.4880884817015
// . . .
}
}
3.10 (d)-dimensionale Vektoren: dot product
Der „Punkt Produkt“ wandelt (d) zwei-dimensionale Vektoren zu einer Reihe.
Der Code unten berechnet einen Punkt Produkt zweier Vektoren:
public class VectorF64
{
// . . .
public static double Dot( VectorF64 a, VectorF64 b )
{
if ((null == a) || (null == b))
{
return (0.0); // Vector not specified.
}
if ((null == a.components) || (null == b.components))
{
return (0.0); // Vector is empty.
}
if (a.Dimensions() != b.Dimensions())
{
return (0.0); // Vectors not the same size.
}
int dimensions = a.Dimensions();
double dotProduct = 0.0;
for (int i = 0; i < dimensions; i++)
{
dotProduct += (a[i] * b[i]);
}
return (dotProduct);
}
// . . .
}
Für jeden Vektor, (A), Length(A) = Sqrt(Dot(A,A)).
3.11 (d)-dimensionale Vektoren: Definition der „parallelen“
Vektoren (A) und (B) sind „parallel,“ wenn alle der folgenden Bedingungen erfüllt sind:
(1) A.Length() > 0;
(2) B.Length() > 0;
(3) Abs(Dot(A,B)) = A.Length()*B.Length().
Der Code unten legt fest, ob ein Paar von Vektoren sind parallel (evtl. Anti-ausgerichtet).
Fließkomma-Zahlen können akkumulieren Fehler in der gebrochene Teil aufgrund der begrenzten Präzision, und deshalb, Computer-Code sollte auch nicht Null-Toleranz beim Vergleich Fließkomma-Zahlen.
Der Code enthält Beispiel-Toleranz-Werte, sondern das Beispiel Toleranz-Werte können nicht angemessen für einige Aufgaben.
public class VectorF64
{
// . . .
public static bool Parallel( VectorF64 a, VectorF64 b )
{
// Smallest normalized float : 1.1754943e-38
// Smallest normalized double: 2.2250738585072020e-308
double nonZeroThreshold = 1.0e-38; // conservative for double
// double: (52+1)-bit mantissa; log10(2^53)=15.95 decimal digits
double fractionalDifferenceThreshold = 1.0e-14; // conservative
if ((null == a) || (null == b))
{
return (false); // Vector is not specified.
}
if ((null == a.components) || (null == b.components))
{
return (false); // Vector is empty.
}
if (a.Dimensions() != b.Dimensions())
{
return (false); // Vectors not the same size.
}
double lengthA = a.Length();
if (lengthA <= nonZeroThreshold)
{
return (false);
}
double lengthB = b.Length();
if (lengthB <= nonZeroThreshold)
{
return (false);
}
double oneImpliesParallel =
Math.Abs( VectorF64.Dot( a, b ) ) / (lengthA * lengthB);
double absoluteDifferenceFromOne =
Math.Abs( oneImpliesParallel - 1.0 );
if (absoluteDifferenceFromOne <= fractionalDifferenceThreshold)
{
return (true);
}
return (false);
}
// . . .
public static void Test()
{
// . . .
// Example of testing for parallel vectors:
// A 6-dimensional vector:
VectorF64 vf = new VectorF64( 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 );
vf.WriteLine(); // ( 0, 1, 2, 3, 4, 5 )
// A 6-dimensional vector:
VectorF64 vg = new VectorF64( 0.0, -2.0, -4.0, -6.0, -8.0, -10.0 );
vg.WriteLine(); // ( 0, -2, -4, -6, -8, -10 )
// Determine if the specified vectors are parallel
// (or "anti-aligned"):
bool parallel = VectorF64.Parallel( vf, vg );
Log.WriteLine( parallel ); // True
// Add a non-negligible displacement to a component of a vector:
vf[0] += 1.0e-5;
vf.WriteLine(); // ( 1E-05, 1, 2, 3, 4, 5 )
// Determine if the specified vectors are parallel
// (or "anti-aligned"):
parallel = VectorF64.Parallel( vf, vg );
Log.WriteLine( parallel ); // False
// . . .
}
}
3.12 (d)-dimensionale Vektoren: Definition der „senkrecht“
Vektoren (A) und (B) „senkrecht zueinander“ stehen, wenn alle der folgenden Bedingungen erfüllt sind:
(1) A.Length() > 0;
(2) B.Length() > 0;
(3) Abs(Dot(A,B)) = 0.
Der Code unten legt fest, ob ein Paar von Vektoren senkrecht zueinander stehen. Fließkomma-Zahlen können akkumulieren Fehler in der gebrochene Teil aufgrund der begrenzten Präzision, und deshalb, Computer-Code sollte auch nicht Null-Toleranz beim Vergleich Fließkomma-Zahlen.
Der Code enthält Beispiel-Toleranz-Werte, sondern das Beispiel Toleranz-Werte können nicht angemessen für einige Aufgaben.
public class VectorF64
{
// . . .
public static bool Perpendicular( VectorF64 a, VectorF64 b )
{
// Smallest normalized float : 1.1754943e-38
// Smallest normalized double: 2.2250738585072020e-308
double nonZeroThreshold = 1.0e-38; // conservative for double
// double: (52+1)-bit mantissa; log10(2^53)=15.95 decimal digits
double fractionalDifferenceThreshold = 1.0e-14; // conservative
if ((null == a) || (null == b))
{
return (false); // Vector is not specified.
}
if ((null == a.components) || (null == b.components))
{
return (false); // Vector is empty.
}
if (a.Dimensions() != b.Dimensions())
{
return (false); // Vectors not the same size.
}
double lengthA = a.Length();
if (lengthA <= nonZeroThreshold)
{
return (false);
}
double lengthB = b.Length();
if (lengthB <= nonZeroThreshold)
{
return (false);
}
double zeroImpliesPerpendicular =
Math.Abs( VectorF64.Dot( a, b ) ) / (lengthA * lengthB);
double absoluteDifferenceFromZero =
Math.Abs( zeroImpliesPerpendicular );
if (absoluteDifferenceFromZero <= fractionalDifferenceThreshold)
{
return (true);
}
return (false);
}
// . . .
public static void Test()
{
// . . .
// Example of testing for perpendicular vectors:
// A 6-dimensional vector:
VectorF64 vf2 = new VectorF64( 0.0, 1.0, 2.0, 0.0, 4.0, 5.0 );
vf2.WriteLine(); // ( 0, 1, 2, 0, 4, 5 )
// A 6-dimensional vector:
VectorF64 vg2 = new VectorF64( 10.0, 0.0, 0.0, -5.0, 0.0, 0.0 );
vg2.WriteLine(); // ( 10, 0, 0, -5, 0, 0 )
// Determine if the specified vectors are perpendicular
bool perpendicular = VectorF64.Perpendicular( vf2, vg2 );
Log.WriteLine( perpendicular ); // True
// Add a non-negligible displacement to a component of a vector:
vf2[0] += 1.0e-13;
vf2.WriteLine(); // ( 1E-13, 1, 2, 0, 4, 5 )
// Determine if the specified vectors are perpendicular
perpendicular = VectorF64.Perpendicular( vf2, vg2 );
Log.WriteLine( perpendicular ); // False
// . . .
}
}
3.13 Matrizen
„Matrix:“ Eine Sammlung von Variablen, so dass jede Variable hat eine einzigartige Kombination aus einer „Zeile“ Namen und ein „Spaltenname.“
„Eintrag:“ Eine Variable innerhalb einer Matrix.
Integer-Werte können als „Folge“ Namen und „Spaltennamen“ für die Variablen in einer Matrix.
Zum Beispiel, wenn eine Matrix hat (totalRows) Zeilen und Spalten (totalColumns), dann wird der Integer-Werte { 0, 1, ..., (totalRows-1) } können die Namen zugeordnet die Reihen, und die Integer-Werte { 0, 1, ..., (totalColumns-1) } können die Namen für die Spalten.
So kann ein Variablen in einer Matrix kann durch Angabe eines Paares von ganzen Zahlen, ( row, column ) und geben dabei die Kombination von Zeile und Spalte Indizes entsprechend der spezifischen Variablen.
Die Größe einer Matrix wird als „(totalRows) * (totalColumns)“ (oder „(totalRows) von (totalColumns)).“
Diese Reihenfolge der Dimensionen ist die gleiche wie die Reihenfolge der Dimensionen verwendet, um Einträge in der Matrix „(( row, column )).“
[Dieses Übereinkommen ist etwas unglücklich, da für viele zwei-dimensionale verwendet (Beispiele: Bilder, Grafiken, etc.) die übliche Konvention ist zu spezifizieren, wie Dimensionen „width * height“ und koordiniert als „( horizontal, vertical )“ (oder „( x, y )).“
Dies ist das Gegenteil von dem die Reihenfolge der Dimensionen und Koordinaten zur Beschreibung Matrizen und ihre Eingaben. ]
Eine Matrix mit (totalRows) gleich (totalColumns) heißt „Quadrat,“ sonst Matrix heißt „rechteckig.“
Eine Matrix kann als mit einer Reihe von „Zeile Vektoren,“ in denen die Variablen in jeder Zeile werden als Zugehörigkeit zu einem Vektor.
Eine Matrix kann auch als mit einer Reihe von „Vektoren Spalte,“ wo die Variablen in jeder Spalte werden als Zugehörigkeit zu einem Vektor.
Matrizen können repräsentieren ein breites Spektrum von mathematischen Beziehungen.
Die Bedeutung einer Matrix, und die Vorgänge, die nicht eher angebracht wäre, für die Bearbeitung der Einträge einer Matrix, hängt vom Kontext ab.
Allerdings gibt es Grundregeln der Arithmetik Matrix, die sich auf vielen Zusammenhängen, und diese Grundregeln wird in einem späteren Abschnitt.
Ein Array und einem (totalColumns) Wert sind ausreichend, um eine Matrix.
Das Array kann (totalRows * totalColumns) Variablen, und der Eintrag in ( row, column ) können entsprechen den Array-Variable mit dem Index ((totalColumns * row) + column).
Der Code unten definiert eine Matrix, mit 64-Bit-Fließkomma-Einträge.
Ein Array und einem (totalColumns) Wert sind ausreichend, um eine Matrix.
Der Code unten ist ein Array und ein (totalColumns) Wert in eine Klasse, nur für Bequemlichkeit.
Der Code unten soll nicht effizient.
Eine Struktur (Beispiel: „struct“, einen Wert Typ) vertreten Matrizen von besonderer Fixformat (Beispiele: 2*2, 3*3 oder 4*4) ist wahrscheinlich zu viel effizienter als die allgemeine Klasse (totalRows * totalColumns) hier gezeigt.
Obwohl Sie den untenstehenden Code definiert eine Matrix mit Floating-Point-Einträge in diesem Artikel bedient sich auch Matrizen mit ganzzahligen Einträgen.
Der Code unten kann leicht modifiziert zur Umsetzung Matrizen mit ganzzahligen Einträgen.
using System;
using System.Collections.Generic;
using System.Text;
// Matrix with 64-bit floating-point entries:
public class MatrixF64
{
private int totalRows;
private int totalColumns;
private double[] entries;
// matrix[ row, column ] = entries[ (totalColumns * row) + column ]
public int Columns()
{
return (this.totalColumns);
}
public int Rows()
{
return (this.totalRows);
}
public MatrixF64()
{
this.totalRows = 0;
this.totalColumns = 0;
this.entries = null;
}
public MatrixF64( int rows, int columns, params double[] paramValues )
{
this.totalRows = 0;
this.totalColumns = 0;
this.entries = null;
if (rows <= 0)
{
return; // Specified an invalid row count.
}
if (columns <= 0)
{
return; // Specified an invalid column count.
}
int totalEntries = (rows * columns);
this.totalRows = rows;
this.totalColumns = columns;
this.entries = new double[totalEntries];
for (int k = 0; k < totalEntries; k++)
{
this.entries[k] = 0.0;
}
if (null == paramValues)
{
return; // No entries specified.
}
int totalParamValues = paramValues.Length;
if (totalParamValues != totalEntries)
{
// The number of specified entries does not match the
// total number of matrix entries.
return;
}
for (int k = 0; k < totalParamValues; k++)
{
this.entries[k] = paramValues[k];
}
}
public MatrixF64( MatrixF64 other )
{
this.totalRows = 0;
this.totalColumns = 0;
this.entries = null;
if (null == other)
{
return;
}
if
(
(null == other.entries)
|| (0 == other.totalRows)
|| (0 == other.totalColumns)
)
{
return;
}
int totalEntries = (other.totalRows * other.totalColumns);
if (other.entries.Length != totalEntries)
{
return;
}
this.totalRows = other.totalRows;
this.totalColumns = other.totalColumns;
this.entries = new double[totalEntries];
for (int k = 0; k < totalEntries; k++)
{
this.entries[k] = other.entries[k];
}
}
public double this[int row, int column]
{
get
{
if
(
(this.totalRows > 0)
&& (this.totalColumns > 0)
&& (null != this.entries)
&& (row >= 0)
&& (row < this.totalRows)
&& (column >= 0)
&& (column < this.totalColumns)
)
{
int k = (this.totalColumns * row) + column;
if ((k >= 0) && (k < this.entries.Length))
{
return (this.entries[k]);
}
}
return (0.0);
}
set
{
if
(
(this.totalRows > 0)
&& (this.totalColumns > 0)
&& (null != this.entries)
&& (row >= 0)
&& (row < this.totalRows)
&& (column >= 0)
&& (column < this.totalColumns)
)
{
int k = (this.totalColumns * row) + column;
if ((k >= 0) && (k < this.entries.Length))
{
this.entries[k] = value;
}
}
}
}
public void WriteLine( int precision )
{
if
(
(this.totalRows <= 0)
|| (this.totalColumns <= 0)
|| (null == this.entries)
)
{
Log.WriteLine( String.Empty + '[' + ' ' + ']' );
return;
}
// Determine the largest entry width in characters
// so that we can make all columns an equal width.
int largestEntryWidth = 1;
for (int i = 0; i < this.totalRows; i++)
{
for (int j = 0; j < this.totalColumns; j++)
{
// { index [,minwidth] [:typeCode[precision]] }
// (minwidth<0) means left-justify
String text = String.Format( String.Empty
+ '{' + '0' + ':' + 'g' + precision + '}', this[i,j] );
if (text.Length > largestEntryWidth)
{
largestEntryWidth = text.Length;
}
}
}
// Print each row of the matrix.
for (int i = 0; i < this.totalRows; i++)
{
Log.Write( '[' );
for (int j = 0; j < this.totalColumns; j++)
{
Log.Write( ' ' );
String text = String.Format( String.Empty
+ '{' + '0' + ',' + largestEntryWidth + ':' + 'g'
+ precision + '}', this[i,j] );
Log.Write( text );
if ((j + 1) < this.totalColumns)
{
Log.Write( ',' );
}
else
{
Log.Write( ' ' );
}
}
Log.WriteLine( ']' );
}
}
public void WriteLine()
{
const int defaultPrecision = 8;
this.WriteLine( defaultPrecision );
}
// . . .
public static void Test()
{
// A 5x3 matrix (5 rows x 3 columns) with
// 64-bit floating-point entries:
MatrixF64 m =
new MatrixF64
(
5, 3,
0.0, 1.0, 2.0, // row 0
3.0, 4.0, 5.0, // row 1
6.0, 7.0, 8.0, // row 2
9.0, 10.0, 11.0, // row 3
12.0, 13.0, 14.0 // row 4
);
m.WriteLine();
// [ 0, 1, 2 ] // row 0
// [ 3, 4, 5 ] // row 1
// [ 6, 7, 8 ] // row 2
// [ 9, 10, 11 ] // row 3
// [ 12, 13, 14 ] // row 4
// . . .
}
}
„Null-Matrix:“ Eine Matrix mit allen Einträgen gleich Null.
public class MatrixF64
{
// . . .
public static MatrixF64 Zero( int rows, int columns )
{
MatrixF64 zero = new MatrixF64( rows, columns );
// The constructor above sets all entries to zero.
return (zero);
}
// . . .
public static void Test()
{
// . . .
// An 8x2 matrix (8 rows x 2 columns) with all 16
// 64-bit floating-point entries set to zero:
MatrixF64 z = MatrixF64.Zero( 8, 2 );
z.WriteLine();
// [ 0, 0 ] // row 0
// [ 0, 0 ] // row 1
// [ 0, 0 ] // row 2
// [ 0, 0 ] // row 3
// [ 0, 0 ] // row 4
// [ 0, 0 ] // row 5
// [ 0, 0 ] // row 6
// [ 0, 0 ] // row 7
// . . .
}
}
3.14 Matrix Addition, Subtraktion, Multiplikation und
Matrix Addition, Subtraktion, Multiplikation und.
public class MatrixF64
{
// . . .
public static MatrixF64 operator +( MatrixF64 a, MatrixF64 b )
{
if ((null == a) || (null == b))
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, a.totalColumns );
if
(
(null == a.entries)
|| (null == b.entries)
|| (a.totalRows != b.totalRows)
|| (a.totalColumns != b.totalColumns)
)
{
return (result);
}
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < a.totalColumns; j++)
{
result[i, j] = a[i, j] + b[i, j];
}
}
return (result);
}
public static MatrixF64 operator -( MatrixF64 a, MatrixF64 b )
{
if ((null == a) || (null == b))
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, a.totalColumns );
if
(
(null == a.entries)
|| (null == b.entries)
|| (a.totalRows != b.totalRows)
|| (a.totalColumns != b.totalColumns)
)
{
return (result);
}
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < a.totalColumns; j++)
{
result[i, j] = a[i, j] - b[i, j];
}
}
return (result);
}
public static MatrixF64 operator -( MatrixF64 a )
{
if (null == a)
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, a.totalColumns );
if (null == a.entries)
{
return (result);
}
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < a.totalColumns; j++)
{
result[i, j] = (-( a[i, j] ));
}
}
return (result);
}
public static MatrixF64 operator *( double scale, MatrixF64 a )
{
if (null == a)
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, a.totalColumns );
if (null == a.entries)
{
return (result);
}
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < a.totalColumns; j++)
{
result[i, j] = scale * a[i, j];
}
}
return (result);
}
public static MatrixF64 operator *( MatrixF64 a, double scale )
{
if (null == a)
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, a.totalColumns );
if (null == a.entries)
{
return (result);
}
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < a.totalColumns; j++)
{
result[i, j] = scale * a[i, j];
}
}
return (result);
}
public static MatrixF64 operator *( MatrixF64 a, MatrixF64 b )
{
if ((null == a) || (null == b))
{
return (null);
}
MatrixF64 result = MatrixF64.Zero( a.totalRows, b.totalColumns );
// The number of columns of the first matrix must be equal
// to the number of rows of the second matrix.
if
(
(null == a.entries)
|| (null == b.entries)
|| (a.totalColumns != b.totalRows)
)
{
return (result);
}
// Entry (i,j) of the result is equal to the dot product
// of row (i) of (a) and column (j) of (b).
for (int i = 0; i < a.totalRows; i++)
{
for (int j = 0; j < b.totalColumns; j++)
{
double dotProduct = 0.0;
for (int k = 0; k < a.totalColumns; k++)
{
dotProduct += (a[i, k] * b[k, j]);
}
result[i, j] = dotProduct;
}
}
return (result);
}
// . . .
public static void Test()
{
// . . .
// Examples of matrix addition, subtraction, and multiplication:
MatrixF64 a =
new MatrixF64
(
3, 5,
0.0, 1.0, 2.0, 3.0, 4.0, // row 0
5.0, 6.0, 7.0, 8.0, 9.0, // row 1
10.0, 11.0, 12.0, 13.0, 14.0 // row 2
);
MatrixF64 b =
new MatrixF64
(
3, 5,
4.0, -3.0, 2.0, -1.0, 0.0, // row 0
-9.0, 8.0, -7.0, 6.0, -5.0, // row 1
14.0, -13.0, 12.0, -11.0, 10.0 // row 2
);
MatrixF64 c =
new MatrixF64
(
5, 3,
0.0, 1.0, 2.0, // row 0
3.0, 4.0, 5.0, // row 1
6.0, 7.0, 8.0, // row 2
9.0, 10.0, 11.0, // row 3
12.0, 13.0, 14.0 // row 4
);
MatrixF64 result = new MatrixF64();
result = a + b;
result.WriteLine();
// [ 4, -2, 4, 2, 4 ]
// [ -4, 14, 0, 14, 4 ]
// [ 24, -2, 24, 2, 24 ]
result = a - b;
result.WriteLine();
// [ -4, 4, 0, 4, 4 ]
// [ 14, -2, 14, 2, 14 ]
// [ -4, 24, 0, 24, 4 ]
result = -a;
result.WriteLine();
// [ 0, -1, -2, -3, -4 ]
// [ -5, -6, -7, -8, -9 ]
// [ -10, -11, -12, -13, -14 ]
result = 3.0 * a;
result.WriteLine();
// [ 0, 3, 6, 9, 12 ]
// [ 15, 18, 21, 24, 27 ]
// [ 30, 33, 36, 39, 42 ]
result = a * c; // (3x5) * (5x3) = (3x3)
result.WriteLine();
// [ 90, 100, 110 ]
// [ 240, 275, 310 ]
// [ 390, 450, 510 ]
result = c * a; // (5x3) * (3x5) = (5x5)
result.WriteLine();
// [ 25, 28, 31, 34, 37 ]
// [ 70, 82, 94, 106, 118 ]
// [ 115, 136, 157, 178, 199 ]
// [ 160, 190, 220, 250, 280 ]
// [ 205, 244, 283, 322, 361 ]
// . . .
}
}
„Einheitsmatrix:“ Eine quadratische Matrix (insgesamt Zeilen entspricht Summe der Spalten) mit Einträgen in Höhe von (1) auf der Diagonalen (Einträge mit einer Reihe gleich Inhalt der jeweiligen Spalte Index), und mit allen anderen Einträgen gleich (0).
Wenn eine quadratische Matrix, (M), multipliziert durch eine „Einheitsmatrix,“ (I), der die gleiche Anzahl von Zeilen (und Spalten), das Produkt ist gleich (M).
Multipliziert (I) von (M) produziert auch ein Produkt gleich (M).
Somit ist die „Einheitsmatrix“ ist analog zu der Zahl „1“ für die Multiplikation der Zahlen (Skalare).
Der untenstehende Code erzeugt eine Einheitsmatrix mit einer bestimmten Anzahl von Zeilen.
public class MatrixF64
{
// . . .
public static MatrixF64 Identity( int rows )
{
MatrixF64 identity = MatrixF64.Zero( rows, rows );
for (int i = 0; i < rows; i++)
{
identity[i, i] = 1.0;
}
return (identity);
}
// . . .
public static void Test()
{
// . . .
// Examples of multiplying by an identity matrix:
MatrixF64 identity3x3 = MatrixF64.Identity( 3 );
// [ 1, 0, 0 ]
// [ 0, 1, 0 ]
// [ 0, 0, 1 ]
MatrixF64 s3x3 =
new MatrixF64
(
3, 3,
0.0, 1.0, 2.0, // row 0
3.0, 4.0, 5.0, // row 1
6.0, 7.0, 8.0 // row 2
);
result = s3x3 * identity3x3;
result.WriteLine();
// [ 0, 1, 2 ]
// [ 3, 4, 5 ]
// [ 6, 7, 8 ]
// (the same as s3x3)
result = identity3x3 * s3x3;
result.WriteLine();
// [ 0, 1, 2 ]
// [ 3, 4, 5 ]
// [ 6, 7, 8 ]
// (the same as s3x3)
// . . .
}
}
3.15 Matrix: LU Factoring; zurück-Substitution
„Matrix LU Factoring:“ Ein Verfahren, die eine quadratische Matrix, (M), dass zwei neue quadratische Matrizen, (L) und (U), nachdem die gleiche Größe wie (M), so dass (L) * (U) = (M), und solche, die Matrix (U) ist „obere Dreiecksmatrix“ (alle Einträge unterhalb der Diagonale sind Null) und Matrix-(L) „niedriger“ ist „dreieckig“ (alle Einträge oberhalb der Diagonalen sind Null).
Matrix LU Factoring kann als Teil eines größeren Verfahrens zur Lösung eines Systems von Gleichungen, oder finden Sie die inverse einer Matrix, oder finden Sie die Determinante einer Matrix.
Matrix LU Factoring ist eine Alternative zur Beseitigung „Gauss-Jordan Verfahren.
Gauss-Jordan Beseitigung erfordert ein System von Gleichungen (A)*(x)=(b), in der Erwägung, dass Factoring nur LU erfordert eine Matrix (A).
Auch nach der Festlegung der LU Factoring einer Matrix (A), ist es sehr leicht zu bestimmen (x) irgendwelche (b).
Das Verfahren zur Lösung für den Vektor (x) in (A)*(x)=(b), da (b) und die LU Factoring (L)*(U)=(A), beinhaltet ein Verfahren, das den Namen „zurück-Substitution“ hier.
Der Code unten beinhaltet ein Verfahren zur Berechnung der LU Faktoren eines Platzes, nicht-singulären Matrix.
Der Code unten beinhaltet ein Verfahren zu tun LU zurück-Substitution.
Achtung: Der Computer-Quelltext berechnet die L und U Faktoren einer Zeile Permeation-Version von einer bestimmten Matrix.
Auch die L und U Matrix Ergebnisse sind in einem einzigen Ausgangs-Matrix.
public class MatrixF64
{
// . . .
public static void FindLUFactorsOfARowPermutedVersionOfAnOriginalMatrix
(
MatrixF64 originalMatrix,
ref MatrixF64 LUFactorsMatrix,
ref int[] rowPermutationOfOriginalMatrix,
ref bool rowExchangeParityIsOdd
)
{
// ''LU factoring'' involves factoring a square matrix (M) in to a
// lower-triangular matrix (L), and an upper-triangular matrix (U),
// such that (L)*(U) = (M).
//
// However, the specified matrix (originalMatrix) might not be
// optimal for direct LU factoring, due to the locations of extreme
// values in the matrix. Therefore, this function instead implicitly
// factors a row-permuted version of originalMatrix, where the
// permutation of rows is determined by the values in the specified
// matrix. The specific row permutation selected by this function is
// returned in an array of row indices (rowPermutationOfOriginalMatrix).
// The resulting (L) and (U) factors are merged in to a single
// output matrix (LUFactorsMatrix). Therefore:
//
// (L part of LUFactorsMatrix) * (U part of LUFactorsMatrix)
// = (originalMatrix with rows permuted according to
// rowPermutationOfOriginalMatrix).
//
// Although factoring a row-permuted version of originalMatrix
// makes it more difficult to interpret the results of this function,
// the resulting LU factoring is likely to be more accurate. In a
// sense, this function indicates (via rowPermutationOfOriginalMatrix)
// how to permute the rows of originalMatrix to be able to produce
// the most accurate LU factoring directly, and this function produces
// that factoring (via the LUFactorsMatrix output).
//
// When using the (L) and (U) matrices (merged in to LUFactorsMatrix)
// to solve a system of equations, it is necessary to permute the rows
// of the solution vector of the system of equations according to
// rowPermutation.
//
// The output matrix (LUFactorsMatrix) is a combination of
// the (L) and (U) matrices:
//
// To form the (L) matrix from (LUFactorsMatrix):
// assume (0) for entries above the diagonal,
// assume (1) for entries on the diagonal,
// and use (LUFactorsMatrix) entries below the diagonal.
//
// To form the (U) matrix from (LUFactorsMatrix):
// assume (0) for entries below the diagonal,
// and use (LUFactorsMatrix) entries on and above the diagonal.
//
// The output array (rowPermutationOfOriginalMatrix) indicates how the
// rows of the original matrix have implicitly (not actually) been
// permuted.
//
// The output boolean (rowExchangeParityIsOdd) indicates if the parity
// of the permutation of rows is odd.
//
// This implementation is partly based on pseudo-code appearing in
// ''Introduction to Algorithms'' (Cormen; Lieserson; Rivest;
// 24th printing, 2000; MIT Press; ISBN 0-262-03141-8).
// See the section named ''Overview of LUP decomposition'', in
// chapter 31 (''Matrix Operations''). The implementation here follows
// the ''LUP-Decomposition'' pseudo-code appearing in a section named
// ''Computing an LUP decomposition''.
//
// This implementation also uses ideas found in the implementation of
// ''ludcmp'' in the book ''Numerical recipes in C : The art of
// scientific computing'' (Press; Teukolsky; Vetterling; Flannery;
// second edition; 1996 reprinting; Cambridge University Press).
// The overall loop structure here resembles that of ''ludcmp''.
// The idea of determining and caching row scale factors in advance of
// finding pivots is used here. The method in ''ludcmp'' to avoid zero
// pivot values has been modified here to handle excessively-small
// pivot values, too. HOWEVER, the row permutation indices produced by
// the following implementation is a true permutation of
// {0,1,...,(totalRows-1)}, whereas ''ludcmp'' (and ''lubksb'')
// interpret their ''permutation indices'' as swaps to perform
// (roughly, for each (i), swap b[ i ] and b[ indx[i] ]). So, the
// following implementation of LU-factoring is not directly compatible
// with ''ludcmp'' or ''lubksb''. Also, the following procedure
// does not destroy the original matrix (unlike ''ludcmp'').
double singularMatrixIfMaxRowElementIsLessThanThis = (1.0e-19);
double forcePivotsToHaveAtLeastThisAbsoluteValue = (1.0e-19);
LUFactorsMatrix = null;
rowPermutationOfOriginalMatrix = null;
rowExchangeParityIsOdd = false;
if (null == originalMatrix)
{
return; // No matrix specified.
}
if
(
(originalMatrix.totalRows <= 0)
|| (originalMatrix.totalColumns <= 0)
|| (null == originalMatrix.entries)
)
{
return; // Matrix is empty.
}
if (originalMatrix.totalRows != originalMatrix.totalColumns)
{
return; // Matrix is not square.
}
// Duplicate the original matrix
LUFactorsMatrix = new MatrixF64( originalMatrix );
// (Lines 2-3 of LUP-Decomposition)
// Initialize the row permutation array to the identity
// permutation.
rowPermutationOfOriginalMatrix = new int[LUFactorsMatrix.totalRows];
for (int i = 0; i < LUFactorsMatrix.totalRows; i++)
{
rowPermutationOfOriginalMatrix[i] = i;
}
// For each row, determine the largest absolute value of
// the row elements, and use the reciprocal as the scale for
// that row.
VectorF64 rowScales = VectorF64.Zero( LUFactorsMatrix.totalRows );
for (int i = 0; i < LUFactorsMatrix.totalRows; i++)
{
double largestElementAbsoluteValueInRow = 0.0;
for (int j = 0; j < LUFactorsMatrix.totalColumns; j++)
{
double absoluteElementValue =
Math.Abs( LUFactorsMatrix[i, j] );
if (absoluteElementValue >
largestElementAbsoluteValueInRow)
{
largestElementAbsoluteValueInRow =
absoluteElementValue;
}
}
if (largestElementAbsoluteValueInRow <
singularMatrixIfMaxRowElementIsLessThanThis)
{
return; // Matrix is singular
}
rowScales[i] = (1.0 / largestElementAbsoluteValueInRow);
}
// (Lines 4-18 of LUP-Decomposition)
// Go through the columns of the matrix.
for (int j = 0; j < LUFactorsMatrix.totalColumns; j++)
{
// (Lines 17-18 of LUP-Decomposition)
// (or equation (2.3.12) of NR)
for (int i = 0; i < j; i++)
{
double sum = LUFactorsMatrix[i, j];
for (int k = 0; k < i; k++)
{
sum -= (LUFactorsMatrix[i, k] * LUFactorsMatrix[k, j]);
}
LUFactorsMatrix[i, j] = sum;
}
// Go through all remaining rows and find the best pivot.
double largestScaledSum = 0.0;
int rowIndexOfLargestScaledSum = j;
for (int i = j; i < LUFactorsMatrix.totalRows; i++)
{
// (equation (2.3.13) of NR)
double sum = LUFactorsMatrix[i, j];
for (int k = 0; k < j; k++)
{
sum -= (LUFactorsMatrix[i, k] * LUFactorsMatrix[k, j]);
}
LUFactorsMatrix[i, j] = sum;
double scaledSum = rowScales[i] * Math.Abs( sum );
if (scaledSum >= largestScaledSum)
{
largestScaledSum = scaledSum;
rowIndexOfLargestScaledSum = i;
}
}
// If indeed we found a better pivot, then exchange rows.
if (j != rowIndexOfLargestScaledSum)
{
// (Line 12 of LUP-Decomposition)
// Exchange the row permutation indices
int tempRowIndex = rowPermutationOfOriginalMatrix[j];
rowPermutationOfOriginalMatrix[j] =
rowPermutationOfOriginalMatrix[rowIndexOfLargestScaledSum];
rowPermutationOfOriginalMatrix[rowIndexOfLargestScaledSum] =
tempRowIndex;
// (Lines 13-14 of LUP-Decomposition)
// Exchange the elements of the rows
for (int k = 0; k < LUFactorsMatrix.totalColumns; k++)
{
double temp = LUFactorsMatrix[rowIndexOfLargestScaledSum, k];
LUFactorsMatrix[rowIndexOfLargestScaledSum, k] =
LUFactorsMatrix[j, k];
LUFactorsMatrix[j, k] = temp;
}
// Exchange the row scale factors
double scaleFactor =
rowScales[rowIndexOfLargestScaledSum];
rowScales[rowIndexOfLargestScaledSum] = rowScales[j];
rowScales[j] = scaleFactor;
// Invert the overall row exchange parity
rowExchangeParityIsOdd = (!(rowExchangeParityIsOdd));
}
// Force the pivot element to have at least a certain
// absolute value.
if (Math.Abs( LUFactorsMatrix[j, j] ) <
forcePivotsToHaveAtLeastThisAbsoluteValue)
{
if (LUFactorsMatrix[j, j] < 0.0)
{
LUFactorsMatrix[j, j] =
(-(forcePivotsToHaveAtLeastThisAbsoluteValue));
}
else
{
LUFactorsMatrix[j, j] =
forcePivotsToHaveAtLeastThisAbsoluteValue;
}
}
// If not the final column, then divide all column elements
// below the diagonal by the pivot element, matrixLU[j,j].
// (Lines 15-16 of LUP-Decomposition)
if (j != (LUFactorsMatrix.totalColumns - 1))
{
double reciprocalOfPivot = (1.0 / LUFactorsMatrix[j, j]);
for (int i = (j + 1); i < LUFactorsMatrix.totalRows; i++)
{
LUFactorsMatrix[i, j] *= reciprocalOfPivot;
}
}
}
}
public static void LUBacksubstitution
(
MatrixF64 LUFactorsMatrix,
int[] rowPermutationOfOriginalMatrix,
VectorF64 givenProductVector,
ref VectorF64 solutionVector
)
{
// This implementation is based on pseudo-code appearing in
// ''Introduction to Algorithms'' (Cormen; Lieserson; Rivest;
// 24th printing, 2000; MIT Press; ISBN 0-262-03141-8).
// See the section named ''Overview of LUP decomposition'', in
// chapter 31 (''Matrix Operations''). The implementation here
// follows the ''LUP-Solve'' pseudo-code appearing in a
// section named ''forward and back substitution''.
solutionVector = null;
if (null == LUFactorsMatrix)
{
return; // No matrix specified.
}
if (null == rowPermutationOfOriginalMatrix)
{
return; // No permutation specified.
}
if (null == givenProductVector)
{
return; // No product vector specified.
}
if
(
(null == LUFactorsMatrix.entries)
|| (LUFactorsMatrix.totalRows <= 0)
|| (LUFactorsMatrix.totalColumns <= 0)
)
{
return; // Matrix is empty.
}
if (LUFactorsMatrix.totalRows !=
LUFactorsMatrix.totalColumns)
{
return; // Matrix is not square.
}
if (givenProductVector.Dimensions() !=
LUFactorsMatrix.totalRows)
{
return; // Product vector size not equal to matrix row count.
}
// Copy the product vector in to the result.
solutionVector = new VectorF64( givenProductVector );
// (See LUP-Solve, lines 2-3)
for (int i = 0; i < LUFactorsMatrix.totalRows; i++)
{
double sum =
givenProductVector[rowPermutationOfOriginalMatrix[i]];
for (int j = 0; j < i; j++)
{
sum -= (LUFactorsMatrix[i, j] * solutionVector[j]);
}
solutionVector[i] = sum;
}
// (See LUP-Solve, lines 4-5)
for (int i = (LUFactorsMatrix.totalRows - 1); i >= 0; i--)
{
double sum = solutionVector[i];
for
(
int j = (i + 1);
j < LUFactorsMatrix.totalColumns;
j++
)
{
sum -= (LUFactorsMatrix[i, j] * solutionVector[j]);
}
double diagonalElement = LUFactorsMatrix[i, i];
if (diagonalElement != 0.0)
{
solutionVector[i] = (sum / diagonalElement);
}
}
}
// . . .
public static void Test()
{
// . . .
// Example of LU factoring and back-substitution:
MatrixF64 a3x3 =
new MatrixF64
(
3, 3,
0.0, 1.0, 2.0, // row 0
3.0, 4.0, 5.0, // row 1
6.0, 7.0, 8.0 // row 2
);
int[] rowPermutationOfOriginalMatrix = null;
bool rowInterchangeParityIsOdd = false;
MatrixF64 a3x3LU = null;
MatrixF64.FindLUFactorsOfARowPermutedVersionOfAnOriginalMatrix
(
a3x3,
ref a3x3LU,
ref rowPermutationOfOriginalMatrix,
ref rowInterchangeParityIsOdd
);
a3x3LU.WriteLine();
// [ 6, 7, 8 ]
// [ 0, 1, 2 ]
// [ 0.5, 0.5, 1e-19 ]
// The matrix above is a combination of the L and U
// matrices. The L matrix is 0 above the diagonal,
// 1 on the diagonal, and the shown values below the
// diagonal. The U matrix is 0 below the diagonal,
// and the shown values on and above the diagonal.
// This LU factoring is of a ROW-PERMUTED version
// of the original matrix.
// The following L and U matrices are manually formed
// by inspecting the LU factoring matrix above.
MatrixF64 L3x3 =
new MatrixF64
(
3, 3,
1.0, 0.0, 0.0, // row 0
0.0, 1.0, 0.0, // row 1
0.5, 0.5, 1.0 // row 2
);
MatrixF64 U3x3 =
new MatrixF64
(
3, 3,
6.0, 7.0, 8.0, // row 0
0.0, 1.0, 2.0, // row 1
0.0, 0.0, 0.0 // row 2
);
// The product (L)*(U) produces a ROW-PERMUTED version
// of the original matrix:
result = L3x3 * U3x3;
result.WriteLine( 4 );
// [ 6, 7, 8 ]
// [ 0, 1, 2 ]
// [ 3, 4, 5 ]
VectorF64 b3x1 = new VectorF64( 1.0, 2.0, 3.0 );
VectorF64 x3x1 = null;
MatrixF64.LUBacksubstitution
(
a3x3LU,
rowPermutationOfOriginalMatrix,
b3x1,
ref x3x1
);
// Solution vector
x3x1.WriteLine();
// ( -0.66666667, 1, 0 )
// Both the given b3x1 and this x3x1 are relative to the
// original matrix, a3x3. The a3x3LU for a ROW-PERMUTED
// version of a3x3, but LUBacksubstitution accepts a
// non-permuted product vector (e.g., b3x1) and produces
// a non-permuted solution vector (e.g., x3x1).
// Check solution by multiplying the original matrix a3x3
// by the solution vector x3x1, to get a product vector.
VectorF64 checkb3x1 = a3x3 * x3x1;
checkb3x1.WriteLine();
// ( 1, 2, 3 )
// This matches the b3x1 vector we specified above, so
// the solution is valid.
// . . .
}
}
3.16 Matrix: Determinante
„Faktor:“ Eine Funktion, wandelt jedes Quadrat (n * n) Matrix (A) zu einer Reihe, det(A), so dass:
(1) Wenn eine Matrix (B) den Austausch von Ergebnissen aus zwei Reihen oder zwei Spalten einer Matrix (A), dann det(B) = (-(det(A)));
(2) Wenn eine Matrix (B) multipliziert ergibt sich aus jeder Zeile, jeder Spalte oder von einer Matrix (A), durch eine Reihe (c), dann det(B) = c * det(A);
(3) Wenn eine Matrix (B) Ergebnisse aus Zugabe ein Vielfaches von einer Zeile zu einer anderen Zeile oder das Hinzufügen von ein Vielfaches von einer Spalte in eine andere Spalte, dann det(B) = det(A).
(4) Wenn (A) ist eine obere Dreiecksmatrix (A[i,j]=0 für alle (i>j)) oder einer unteren Dreiecksmatrix (A[i,j]=0 für alle (i<j)), dann det(A) = (A[0,0] * A[1,1] * ... * A[n-1,n-1]);
(Zum Beispiel, die Determinante einer Matrix Identität ist ein; det(I) = 1.)
Regeln (1), (2) und (3), können verwendet werden, in einem Prozess namens „Gaussian Beseitigung, zu konvertieren, jede quadratische Matrix zu einer Dreiecksmatrix.
Artikel (4) kann verwendet werden, um die Determinante einer Dreiecksmatrix.
Die formale Definition einer „Determinante:“
Lassen Sie M[n](K) bezeichnen die Menge aller (n * n) Matrizen über dem Gebiet (K).
Die „Determinante“ ist die einzigartige Funktion (F) so dass F:M[n](K) --> K mit zwei Attribute:
(1) F ist abwechselnd multilinearen im Hinblick auf die Spalten (oder Zeilen), und
(2) F( I ) = 1.
Ein „multilinearen“ (D) Funktion eines (n * n) Matrix (A) kann wie folgt geschrieben werden: D(A) = Sum( A[0,k[0]] * A[1,k[1]] * ... * A[n-1,k[n-1]] * D( e[k[0]], e[k[1]], ..., e[k[n-1]] ) ), wenn die Summe ist, übernommen (n^n) alle Kombinationen von 0 <= k[i] <= (n-1), und wo e[j] Zeile repräsentiert (j) der Einheitsmatrix.
Eine solche Funktion kann zeichnet sich durch die Werte der D( e[k[0]], e[k[1]], ..., e[k[n-1]] ).
Eine „alternierende multilinearen“ Funktion ist eine Funktion multilinearen, (D), so dass D( ..., e[i], ..., e[i], ... ) = 0 und D( ..., e[i], e[j], ... ) = (-( D( ..., e[j], e[i], ... ) )).
So, Swapping Spalten (bzw. Zeilen) ändert sich das Vorzeichen der Funktion.
Auch der Begriff D( e[k[0]], ..., e[k[n-1]] ) ist gleich Null, wenn die Kombination von k[i] Werte nicht der Beteiligung aller eindeutige Werte, wenn die Menge von Werten ist nicht eine Permutation der Zahlen {0,1,...,(n-1)}.
Vermietung D( I ) = 1 (D( e[0], e[1], ..., e[n-1] ) = 1), in Kombination mit der alternierenden multilinearen Attribute beschrieben, bedeutet, dass alle Werte der alternierenden multilinearen D( e[k[0]], e[k[1]], ..., e[k[n-1]] ) Funktion bestimmt werden können.
Die Funktion wird nicht null nur, wenn der Satz von k[i] Werte ist eine Permutation von {0,1,...,(n-1)}, und sie haben eine Größenordnung von ein, wenn sie nicht null ist, und dem Zeichen wird die Zahl der Swaps erforderlich, um die Permutation auf die Identität Permutation.
Der Wert einer Determinante einer (n * n) Matrix (A) lassen sich durch Addition der Produkte der Form (sgn(p) * A[0,p[0]] * A[1,p[1]] * ... * A[(n-1),p[n-1]]) für jeden und jede Permutation (p) der Zahlen {0,1,2,...,(n-1)}.
Der Begriff bezeichnet (sgn(p)) die „Unterschrift“ (oder Swap-Anzahl) der Permutation (p), wo (sgn(p)) ist gleich (+1) wenn (p) ist eine „Permutation selbst,“ und ist gleich (-1) wenn (p) ist eine „ungerade Permutation.“
Da gibt es (n!) Permutationen der Zahlen {0,1,2,...,(n-1)}, diese Formel hat (n!) summands.
Andere Verfahren zur Berechnung der Determinante (Beispiele: die Einbeziehung Gaussian Beseitigung oder LU Factoring, oder die Expansion von Minderjährigen, etc.) Berechnung implizit eine Hierarchie von Zwischenprodukten Mengen, mit denen diese Verfahren zur Berechnung der Determinante in einer Größenordnung von (n^3) Schritte.
Wenn die Determinante einer Matrix gleich Null ist, die Matrix nicht über eine inverse.
Wenn die Determinante einer Matrix ist ungleich Null, die Matrix hat eine inverse.
Wenn eine Matrix repräsentiert die Koeffizienten von einer linearen Gleichungssystem, und die Determinante der Matrix gleich Null ist, dann das Gleichungssystem nicht über eine einzigartige Lösung.
Wenn eine Matrix repräsentiert die Koeffizienten von einer linearen Gleichungssystem, und die Determinante der Matrix nicht null ist, dann das Gleichungssystem hat eine eindeutige Lösung.
Der Code unten enthält Verfahren zur Berechnung der Determinante einer quadratischen Matrix.
Determinanten für 1*1, 2*2, 3*3 und 4*4 Matrizen als Ergebnis von expliziten Formeln.
Verwenden explizite Formeln zur Berechnung der Determinanten für kleine Matrizen ist wahrscheinlich, dass Compute-Ergebnisse schneller als mit einem allgemeinen Verfahren zur Berechnung dieser Faktoren.
Das Verfahren für den allgemeinen Fall verwendet LU Factoring, aber es gibt viele andere Methoden der Informatik Determinanten.
public class MatrixF64
{
// . . .
private double Determinant1x1()
{
if
(
(this.totalRows != 1)
|| (this.totalColumns != 1)
|| (null == this.entries)
)
{
return (0.0);
}
// The determinant of a 1x1 matrix is simply
// the value of the single element.
return ( this[0,0] );
}
private double Determinant2x2()
{
if
(
(this.totalRows != 2)
|| (this.totalColumns != 2)
|| (null == this.entries)
)
{
return (0.0);
}
return
(
+ this[0,0] * this[1,1]
- this[0,1] * this[1,0]
);
}
private double Determinant3x3()
{
if
(
(this.totalRows != 3)
|| (this.totalColumns != 3)
|| (null == this.entries)
)
{
return (0.0);
}
return
(
+ this[0,0] * this[1,1] * this[2,2]
+ this[0,1] * this[1,2] * this[2,0]
+ this[0,2] * this[1,0] * this[2,1]
- this[0,0] * this[1,2] * this[2,1]
- this[0,1] * this[1,0] * this[2,2]
- this[0,2] * this[1,1] * this[2,0]
);
}
private double Determinant4x4()
{
if
(
(this.totalRows != 4)
|| (this.totalColumns != 4)
|| (null == this.entries)
)
{
return (0.0);
}
return
(
+ this[0, 0] * this[1, 1] * this[2, 2] * this[3, 3]
+ this[0, 0] * this[1, 2] * this[2, 3] * this[3, 1]
+ this[0, 0] * this[1, 3] * this[2, 1] * this[3, 2]
+ this[0, 1] * this[1, 0] * this[2, 3] * this[3, 2]
+ this[0, 1] * this[1, 2] * this[2, 0] * this[3, 3]
+ this[0, 1] * this[1, 3] * this[2, 2] * this[3, 0]
+ this[0, 2] * this[1, 0] * this[2, 1] * this[3, 3]
+ this[0, 2] * this[1, 1] * this[2, 3] * this[3, 0]
+ this[0, 2] * this[1, 3] * this[2, 0] * this[3, 1]
+ this[0, 3] * this[1, 0] * this[2, 2] * this[3, 1]
+ this[0, 3] * this[1, 1] * this[2, 0] * this[3, 2]
+ this[0, 3] * this[1, 2] * this[2, 1] * this[3, 0]
- this[0, 0] * this[1, 1] * this[2, 3] * this[3, 2]
- this[0, 0] * this[1, 2] * this[2, 1] * this[3, 3]
- this[0, 0] * this[1, 3] * this[2, 2] * this[3, 1]
- this[0, 1] * this[1, 0] * this[2, 2] * this[3, 3]
- this[0, 1] * this[1, 2] * this[2, 3] * this[3, 0]
- this[0, 1] * this[1, 3] * this[2, 0] * this[3, 2]
- this[0, 2] * this[1, 0] * this[2, 3] * this[3, 1]
- this[0, 2] * this[1, 1] * this[2, 0] * this[3, 3]
- this[0, 2] * this[1, 3] * this[2, 1] * this[3, 0]
- this[0, 3] * this[1, 0] * this[2, 1] * this[3, 2]
- this[0, 3] * this[1, 1] * this[2, 2] * this[3, 0]
- this[0, 3] * this[1, 2] * this[2, 0] * this[3, 1]
);
}
public double Determinant()
{
if
(
(this.totalRows <= 0)
|| (this.totalColumns <= 0)
|| (null == this.entries)
)
{
return (0.0); // Matrix is empty.
}
if (this.totalRows != this.totalColumns)
{
// Matrix is not square
return (0.0);
}
// Simple cases
if (1 == this.totalRows) { return (this.Determinant1x1()); }
if (2 == this.totalRows) { return (this.Determinant2x2()); }
if (3 == this.totalRows) { return (this.Determinant3x3()); }
if (4 == this.totalRows) { return (this.Determinant4x4()); }
int[] rowPermutationOfOriginalMatrix = null;
bool rowExchangeParityIsOdd = false;
MatrixF64 originalMatrix = new MatrixF64( this );
MatrixF64 LUFactorsMatrix = null;
MatrixF64.FindLUFactorsOfARowPermutedVersionOfAnOriginalMatrix
(
originalMatrix,
ref LUFactorsMatrix,
ref rowPermutationOfOriginalMatrix,
ref rowExchangeParityIsOdd
);
if ((null == LUFactorsMatrix)
|| (null == rowPermutationOfOriginalMatrix))
{
return (0.0);
}
double determinant = 1.0;
if (true == rowExchangeParityIsOdd)
{
determinant = (-1.0);
}
for (int j = 0; j < LUFactorsMatrix.totalColumns; j++)
{
determinant *= LUFactorsMatrix[j, j];
}
return (determinant);
}
// . . .
public static void Test()
{
// . . .
// Examples of matrix determinants:
MatrixF64 d2x2 =
new MatrixF64
(
2, 2,
2.0, 5.0, // row 0
-4.0, -7.0 // row 1
);
double detd2x2 = d2x2.Determinant();
Log.WriteLine( detd2x2 );
// detd2x2 = 6
MatrixF64 d3x3 =
new MatrixF64
(
3, 3,
2.0, 5.0, 7.0, // row 0
-4.0, -1.0, 6.0, // row 1
9.0, 8.0, 3.0 // row 2
);
double detd3x3 = d3x3.Determinant();
Log.WriteLine( detd3x3 );
// detd3x3 = 67
MatrixF64 d4x4 =
new MatrixF64
(
4, 4,
7.0, -5.0, 2.0, 4.0, // row 0
3.0, 2.0, 6.0, 3.0, // row 1
-9.0, 8.0, -3.0, 2.0, // row 2
5.0, 3.0, 2.0, 5.0 // row 3
);
double detd4x4 = d4x4.Determinant();
Log.WriteLine( detd4x4 );
// detd4x4 = 1457
// . . .
}
}
3.17 (d)-dimensionale Vektoren: Kreuzprodukt
„Kreuzprodukt:“ Eine Funktion im Zusammenhang mit einem (d)-dimensionalen Raum, wandelt eine geordnete Liste von (d-1) (d)-dimensionale Vektoren, { A(0), A(1), ..., A(d-2) }, zu einem (d)-dimensionaler Vektor, so dass die Funktion hat die Attribute unter:
(1) Wenn A(i)=0 (Null-Vektor), für jede 0 <= i <= (d-2), dann Cross( A(0), A(1), ..., A(d-2) )=0 (Null-Vektor).
Das Kreuzprodukt von einer geordneten Liste von Vektoren ist gleich Null, wenn jeder Vektor in dieser Liste gleich Null ist;
(2) Wenn ein Paar von Vektoren, A(i) und A(j), für jede (i!=j) mit 0 <= i <= (d-2) und 0 <= j <= (d-2), sind parallel (Abs(Dot(A(i),A(j))) = Length(A(i)) * Length(A(j))), dann Cross( A(0), A(1), ..., A(d-2) )=0 (Null-Vektor).
Das Kreuzprodukt von einer geordneten Liste von Vektoren ist gleich Null, wenn zwei beliebige Vektoren in dieser Liste sind parallel;
(3) Wenn jeder Vektor A(i) ist ungleich Null, und wenn A(i) ist nicht parallel zu A(j) für alle (i!=j) mit 0 <= i <= (d-2) und 0 <= j <= (d-2), dann B = Cross( A(0), A(1), ..., A(d-2) ) ist so groß, dass Dot(A(i),B)=0 für alle 0 <= i <= (d-2).
Wenn das Kreuzprodukt von einer geordneten Liste von Vektoren ist ungleich Null, dann das Kreuzprodukt Ergebnis ist im rechten Winkel zu jedem Vektor in die geordnete Liste von Vektoren;
(4) Wenn (c) ist ein numerischer Wert, dann Cross( (c * A(0)), A(1), ..., A(d-2) ) = Cross( A(0), (c * A(1)), ..., A(d-2) ) = ... = Cross( A(0), A(1), ..., (c * A(d-2)) ) = c * Cross( A(0), A(1), ..., A(d-2) ).
Da eine geordnete Liste von Vektoren, das Kreuzprodukt der geordneten Liste von Vektoren mit einem Vektor multipliziert mit einem bestimmten Zahlenwert entspricht Multiplikation der Kreuzprodukt der geordneten Liste von Vektoren durch den numerischen Wert;
(5) Angesichts der Tatsache, zwei Nummern (i) und (j), wo (i!=j) und 0 <= i <= (d-2) und 0 <= j <= (d-2), und eine geordnete Liste der (d-1) (d)-dimensionale Vektoren, { A(0), A(1), ..., A(d-2) }, und ein weiteres geordnete Liste von (d-1) (d)-dimensionale Vektoren, { B(0), B(1), ..., B(d-2) }, so dass für alle B(k) = A(k) mit ((k != i) && (k != j)) 0 <= k <= (d-2), und solche, die B(i) = A(j) und B(j) = A(i), dann Cross( A(0), A(1), ..., A(d-2) ) = (-1) * Cross( B(0), B(1), ..., B(d-2) ).
Wenn das Kreuzprodukt von einer geordneten Liste von Vektoren ist ungleich Null, dann das Kreuzprodukt hat das Gegenteil Kreuzzeichen Produkt des gleichen geordnete Liste von Vektoren mit Ausnahme von genau zwei Vektoren ausgetauscht werden (vertauscht).
Angesichts Grundlage Vektoren e(k), für 0 <= k <= (d-1), (d)-dimensionalen Raum, das Kreuzprodukt von einer geordneten Liste der (d-1) Vektoren lassen sich durch Berechnung der Determinante der Matrix (d*d) unten:
e(0), e(1), ..., e(d-1),
A( 0 )[0], A( 0 )[1], ..., A( 0 )[d-1],
A( 1 )[0], A( 1 )[1], ..., A( 1 )[d-1],
...,
A(d-2)[0], A(d-2)[1], ..., A(d-2)[d-1]
Die Determinante der Matrix, dass in Zukunft nicht mit einer Zahl, sondern stattdessen einen Vektor, wo wird es numerischen Koeffizienten auf der Grundlage (d) Vektoren.
Für zwei-dimensionalen Raum, und den Vektor A = ( ax, ay ):
Cross( A )
= Determinant
(
e(x), e(y),
ax, ay
)
= ay * e(x)
- ax * e(y)
= ( ay, -ax )
Das Ergebnis ist Vektor senkrecht zur einzigen Input Vektor.
Wenn die x-y Flugzeug angesehen, so dass x steigt in einem rechts Richtung y und erhöht in einer Richtung nach oben, dann das Kreuzprodukt Ergebnis ist ein Viertel-Umdrehung im „Uhrzeigersinn“ relativ zum Input-Vektor.
Beispiele für Cross-Produkte in zwei-dimensionalen Raum:
Cross( e(x) ) = (-1) * e(y)
Cross( e(y) ) = e(x)
Für drei-dimensionalen Raum, und die Vektoren A = ( ax, ay, az ) und B = ( bx, by, bz ):
Cross( A, B )
= Determinant
(
e(x), e(y), e(z),
ax, ay, az,
bx, by, bz
)
= (ay*bz - az*by) * e(x)
+ (az*bx - ax*bz) * e(y)
+ (ax*by - ay*bx) * e(z)
= ( (ay*bz - az*by), (az*bx - ax*bz), (ax*by - ay*bx) )
Beispiele für Cross-Produkte im dreidimensionalen Raum:
Cross( e(x), e(y) ) = e(z)
Cross( e(y), e(x) ) = (-1) * e(z)
Cross( e(y), e(z) ) = e(x)
Cross( e(z), e(y) ) = (-1) * e(x)
Cross( e(z), e(x) ) = e(y)
Cross( e(x), e(z) ) = (-1) * e(y)
Für vier-dimensionalen Raum, und die Vektoren A = ( ax, ay, az, aw ), B = ( bx, by, bz, bw ) und C = ( cx, cy, cz, cw ):
Cross( A, B, C )
= Determinant
(
e(x), e(y), e(z), e(w),
ax, ay, az, aw,
bx, by, bz, bw,
cx, cy, cz, cw
)
= ( ay*bz*cw - ay*bw*cz + az*bw*cy - az*by*cw + aw*by*cz - aw*bz*cy) * e(x)
+ (-ax*bz*cw + ax*bw*cz - az*bw*cx + az*bx*cw - aw*bx*cz + aw*bz*cx) * e(y)
+ ( ax*by*cw - ax*bw*cy + ay*bw*cx - ay*bx*cw + aw*bx*cy - aw*by*cx) * e(z)
+ (-ax*by*cz + ax*bz*cy - ay*bz*cx + ay*bx*cz - az*bx*cy + az*by*cx) * e(w)
= ( ( ay*bz*cw - ay*bw*cz + az*bw*cy - az*by*cw + aw*by*cz - aw*bz*cy),
(-ax*bz*cw + ax*bw*cz - az*bw*cx + az*bx*cw - aw*bx*cz + aw*bz*cx),
( ax*by*cw - ax*bw*cy + ay*bw*cx - ay*bx*cw + aw*bx*cy - aw*by*cx),
(-ax*by*cz + ax*bz*cy - ay*bz*cx + ay*bx*cz - az*bx*cy + az*by*cx) )
Beispiele für Cross-Produkte in vier-dimensionalen Raum:
Cross( e(x), e(y), e(z) ) = (-1) * e(w)
Cross( e(x), e(z), e(y) ) = e(w)
Cross( e(y), e(x), e(z) ) = e(w)
Cross( e(y), e(z), e(x) ) = (-1) * e(w)
Cross( e(z), e(x), e(y) ) = (-1) * e(w)
Cross( e(z), e(y), e(x) ) = e(w)
Cross( e(x), e(y), e(w) ) = e(z)
Cross( e(x), e(w), e(y) ) = (-1) * e(z)
Cross( e(y), e(x), e(w) ) = (-1) * e(z)
Cross( e(y), e(w), e(x) ) = e(z)
Cross( e(w), e(x), e(y) ) = e(z)
Cross( e(w), e(y), e(x) ) = (-1) * e(z)
Cross( e(x), e(z), e(w) ) = (-1) * e(y)
Cross( e(x), e(w), e(z) ) = e(y)
Cross( e(z), e(x), e(w) ) = e(y)
Cross( e(z), e(w), e(x) ) = (-1) * e(y)
Cross( e(w), e(x), e(z) ) = (-1) * e(y)
Cross( e(w), e(z), e(x) ) = e(y)
Cross( e(y), e(z), e(w) ) = e(x)
Cross( e(y), e(w), e(z) ) = (-1) * e(x)
Cross( e(z), e(y), e(w) ) = (-1) * e(x)
Cross( e(z), e(w), e(y) ) = e(x)
Cross( e(w), e(y), e(z) ) = e(x)
Cross( e(w), e(z), e(y) ) = (-1) * e(x)
Der Code unten berechnet das Kreuz-Produkt von zwei oder mehr Vektoren.
Der Test Beispiele zeigen, dass die Nicht-Null-Ergebnis Vektoren sind senkrecht zur Gesamtheit der Input-Vektoren.
public class MatrixF64
{
// . . .
public static VectorF64 Cross( params VectorF64[] parameterArray )
{
if (null == parameterArray)
{
return(new VectorF64());
}
int totalVectors = parameterArray.Length;
// The number of dimensions of the space must be one larger
// than the number of specified vectors.
int dimensions = (1 + totalVectors);
if (dimensions < 2)
{
// The cross product does not exist for space with
// fewer than two dimensions.
return (new VectorF64());
}
// All specified vectors must exist and must have a number
// of components equal to the number of dimensions of the space.
for (int k = 0; k < totalVectors; k++)
{
if (null == parameterArray[k])
{
// Vector missing.
return (new VectorF64());
}
else if (null == parameterArray[k].components)
{
// Vector is empty.
return (new VectorF64());
}
else if (parameterArray[k].components.Length != dimensions)
{
// Vector has the wrong number of components.
return (new VectorF64());
}
}
// Form a (d*d) matrix with the (d-1) supplied vectors
// forming the final (d-1) rows of the matrix.
MatrixF64 matrix = MatrixF64.Zero( dimensions, dimensions );
for (int i = 1; i < dimensions; i++)
{
VectorF64 rowVector = parameterArray[ i - 1 ];
for (int j = 0; j < dimensions; j++)
{
matrix[i, j] = rowVector[j];
}
}
VectorF64 result = VectorF64.Zero( dimensions );
// For each component of the result vector, set the first
// row of the matrix to a basis vector, compute the matrix
// determinant, and use the resulting value as the
// component of the result vector. This is inefficient,
// due to the determinant being computed (d) times.
// Clearly it would be better to have explicit formulas
// for 2-dimensional, 3-dimensional, and 4-dimensional
// cross-products.
for (int i = 0; i < dimensions; i++)
{
// Set the first row of the matrix to be the basis
// vector for the coordinate axis with index (i).
for (int j = 0; j < dimensions; j++)
{
matrix[0, j] = 0.0;
}
matrix[0, i] = 1.0;
// Compute the determinant of the modified matrix.
double determinant = matrix.Determinant();
// Set component (i) of the result to the determinant
// that was computed.
result[i] = determinant;
}
return (result);
}
// . . .
public static void Test()
{
// . . .
// Examples of cross products:
// Three-dimensional example:
VectorF64 vcp1x3a = new VectorF64( 1.0, 2.0, 3.0 );
VectorF64 vcp1x3b = new VectorF64( 3.0, 1.0, 2.0 );
VectorF64 vcp1x3 = VectorF64.Cross( vcp1x3a, vcp1x3b );
vcp1x3.WriteLine(); // ( 1, 7, -5 )
// Verify that the result is perpendicular to the original
// vectors:
Log.WriteLine( VectorF64.Perpendicular( vcp1x3, vcp1x3a ) );
Log.WriteLine( VectorF64.Perpendicular( vcp1x3, vcp1x3b ) );
// True, True
// Four-dimensional example:
VectorF64 vcp1x4a = new VectorF64( 1.0, 2.0, 3.0, 4.0 );
VectorF64 vcp1x4b = new VectorF64( 4.0, 1.0, 2.0, 3.0 );
VectorF64 vcp1x4c = new VectorF64( 3.0, 4.0, 1.0, 2.0 );
VectorF64 vcp1x4 = VectorF64.Cross( vcp1x4a, vcp1x4b, vcp1x4c );
vcp1x4.WriteLine(); // ( 4, 4, 44, -36 )
// Verify that the result is perpendicular to the original
// vectors:
Log.WriteLine( VectorF64.Perpendicular( vcp1x4, vcp1x4a ) );
Log.WriteLine( VectorF64.Perpendicular( vcp1x4, vcp1x4b ) );
Log.WriteLine( VectorF64.Perpendicular( vcp1x4, vcp1x4c ) );
// True, True, True
// . . .
}
}
3.18 Matrix: inverse
„inversen Matrix:“ Eine quadratische Matrix in Verbindung mit einem anderen quadratische Matrix der gleichen Größe, so dass das Produkt der beiden Matrizen ist die „Einheitsmatrix.“
Wenn eine Matrix hat eine zugehörige inverse Matrix, gibt es nur eine solche inverse Matrix.
Wenn die Determinante einer Matrix gleich Null ist, dann die Matrix heißt „singulär,“ und die Matrix nicht über eine zugehörige inverse Matrix.
Der Code unten wird die inverse von jedem Platz, nicht-singulären Matrix.
Inversen für 1*1, 2*2, 3*3 und 4*4 Matrizen als Ergebnis von expliziten Formeln.
Verwenden explizite Formeln könnte schneller als mit einem allgemeinen Verfahren.
Die explizite Formeln sind auch zuverlässig.
Das Verfahren für den allgemeinen Fall verwendet LU Factoring, aber es gibt viele andere Methoden der Informatik Inversen von Matrizen.
public class MatrixF64
{
// . . .
private MatrixF64 Inverse1x1()
{
if
(
(this.totalRows != 1)
|| (this.totalColumns != 1)
|| (null == this.entries)
)
{
return (MatrixF64.Zero( 1, 1 )); // Matrix is empty.
}
if (0.0 == this[0,0])
{
return ( MatrixF64.Zero( 1, 1 ) ); // Matrix has no inverse.
}
MatrixF64 inverse = MatrixF64.Zero( 1, 1 );
// The inverse of a 1x1 matrix is simply the
// reciprocal of the single entry.
inverse[0,0] = (1.0 / this[0,0]);
return (inverse);
}
private MatrixF64 Inverse2x2()
{
if
(
(this.totalRows != 2)
|| (this.totalColumns != 2)
|| (null == this.entries)
)
{
return (MatrixF64.Zero( 2, 2 )); // Matrix is empty.
}
double determinant = this.Determinant2x2();
if (0.0 == determinant)
{
return (MatrixF64.Zero( 2, 2 )); // Matrix has no inverse.
}
MatrixF64 inverse = MatrixF64.Zero( 2, 2 );
inverse[0,0] = this[1,1];
inverse[0,1] = (-(this[0,1]));
inverse[1,0] = (-(this[1,0]));
inverse[1,1] = this[0,0];
double factor = (1.0 / determinant);
for ( int i = 0; i < inverse.totalRows; i++ )
{
for ( int j = 0; j < inverse.totalColumns; j++ )
{
inverse[i,j] *= factor;
}
}
return (inverse);
}
private MatrixF64 Inverse3x3()
{
if
(
(this.totalRows != 3)
|| (this.totalColumns != 3)
|| (null == this.entries)
)
{
return (MatrixF64.Zero( 3, 3 )); // Matrix is empty.
}
double determinant = this.Determinant3x3();
if (0.0 == determinant)
{
return (MatrixF64.Zero( 3, 3 )); // Matrix has no inverse.
}
MatrixF64 inverse = MatrixF64.Zero( 3, 3 );
inverse[0, 0] =
this[1, 1] * this[2, 2]
- this[1, 2] * this[2, 1];
inverse[0, 1] =
this[0, 2] * this[2, 1]
- this[0, 1] * this[2, 2];
inverse[0, 2] =
this[0, 1] * this[1, 2]
- this[0, 2] * this[1, 1];
inverse[1, 0] =
this[1, 2] * this[2, 0]
- this[1, 0] * this[2, 2];
inverse[1, 1] =
this[0, 0] * this[2, 2]
- this[0, 2] * this[2, 0];
inverse[1, 2] =
this[0, 2] * this[1, 0]
- this[0, 0] * this[1, 2];
inverse[2, 0] =
this[1, 0] * this[2, 1]
- this[1, 1] * this[2, 0];
inverse[2, 1] =
this[0, 1] * this[2, 0]
- this[0, 0] * this[2, 1];
inverse[2, 2] =
this[0, 0] * this[1, 1]
- this[0, 1] * this[1, 0];
double factor = (1.0 / determinant);
for (int i = 0; i < inverse.totalRows; i++)
{
for (int j = 0; j < inverse.totalColumns; j++)
{
inverse[i, j] *= factor;
}
}
return (inverse);
}
private MatrixF64 Inverse4x4()
{
if
(
(this.totalRows != 4)
|| (this.totalColumns != 4)
|| (null == this.entries)
)
{
return (MatrixF64.Zero( 4, 4 )); // Matrix is empty.
}
double determinant = this.Determinant4x4();
if (0.0 == determinant)
{
return (MatrixF64.Zero( 4, 4 )); // Matrix has no inverse.
}
MatrixF64 inverse = MatrixF64.Zero( 4, 4 );
inverse[0,0] =
+ this[1,1] * this[2,2] * this[3,3]
+ this[1,2] * this[2,3] * this[3,1]
+ this[1,3] * this[2,1] * this[3,2]
- this[1,1] * this[2,3] * this[3,2]
- this[1,2] * this[2,1] * this[3,3]
- this[1,3] * this[2,2] * this[3,1];
inverse[0,1] =
+ this[0,1] * this[2,3] * this[3,2]
+ this[0,2] * this[2,1] * this[3,3]
+ this[0,3] * this[2,2] * this[3,1]
- this[0,1] * this[2,2] * this[3,3]
- this[0,2] * this[2,3] * this[3,1]
- this[0,3] * this[2,1] * this[3,2];
inverse[0,2] =
+ this[0,1] * this[1,2] * this[3,3]
+ this[0,2] * this[1,3] * this[3,1]
+ this[0,3] * this[1,1] * this[3,2]
- this[0,1] * this[1,3] * this[3,2]
- this[0,2] * this[1,1] * this[3,3]
- this[0,3] * this[1,2] * this[3,1];
inverse[0,3] =
+ this[0,1] * this[1,3] * this[2,2]
+ this[0,2] * this[1,1] * this[2,3]
+ this[0,3] * this[1,2] * this[2,1]
- this[0,1] * this[1,2] * this[2,3]
- this[0,2] * this[1,3] * this[2,1]
- this[0,3] * this[1,1] * this[2,2];
inverse[1,0] =
+ this[1,0] * this[2,3] * this[3,2]
+ this[1,2] * this[2,0] * this[3,3]
+ this[1,3] * this[2,2] * this[3,0]
- this[1,0] * this[2,2] * this[3,3]
- this[1,2] * this[2,3] * this[3,0]
- this[1,3] * this[2,0] * this[3,2];
inverse[1,1] =
+ this[0,0] * this[2,2] * this[3,3]
+ this[0,2] * this[2,3] * this[3,0]
+ this[0,3] * this[2,0] * this[3,2]
- this[0,0] * this[2,3] * this[3,2]
- this[0,2] * this[2,0] * this[3,3]
- this[0,3] * this[2,2] * this[3,0];
inverse[1,2] =
+ this[0,0] * this[1,3] * this[3,2]
+ this[0,2] * this[1,0] * this[3,3]
+ this[0,3] * this[1,2] * this[3,0]
- this[0,0] * this[1,2] * this[3,3]
- this[0,2] * this[1,3] * this[3,0]
- this[0,3] * this[1,0] * this[3,2];
inverse[1,3] =
+ this[0,0] * this[1,2] * this[2,3]
+ this[0,2] * this[1,3] * this[2,0]
+ this[0,3] * this[1,0] * this[2,2]
- this[0,0] * this[1,3] * this[2,2]
- this[0,2] * this[1,0] * this[2,3]
- this[0,3] * this[1,2] * this[2,0];
inverse[2,0] =
+ this[1,0] * this[2,1] * this[3,3]
+ this[1,1] * this[2,3] * this[3,0]
+ this[1,3] * this[2,0] * this[3,1]
- this[1,0] * this[2,3] * this[3,1]
- this[1,1] * this[2,0] * this[3,3]
- this[1,3] * this[2,1] * this[3,0];
inverse[2,1] =
+ this[0,0] * this[2,3] * this[3,1]
+ this[0,1] * this[2,0] * this[3,3]
+ this[0,3] * this[2,1] * this[3,0]
- this[0,0] * this[2,1] * this[3,3]
- this[0,1] * this[2,3] * this[3,0]
- this[0,3] * this[2,0] * this[3,1];
inverse[2,2] =
+ this[0,0] * this[1,1] * this[3,3]
+ this[0,1] * this[1,3] * this[3,0]
+ this[0,3] * this[1,0] * this[3,1]
- this[0,0] * this[1,3] * this[3,1]
- this[0,1] * this[1,0] * this[3,3]
- this[0,3] * this[1,1] * this[3,0];
inverse[2,3] =
+ this[0,0] * this[1,3] * this[2,1]
+ this[0,1] * this[1,0] * this[2,3]
+ this[0,3] * this[1,1] * this[2,0]
- this[0,0] * this[1,1] * this[2,3]
- this[0,1] * this[1,3] * this[2,0]
- this[0,3] * this[1,0] * this[2,1];
inverse[3,0] =
+ this[1,0] * this[2,2] * this[3,1]
+ this[1,1] * this[2,0] * this[3,2]
+ this[1,2] * this[2,1] * this[3,0]
- this[1,0] * this[2,1] * this[3,2]
- this[1,1] * this[2,2] * this[3,0]
- this[1,2] * this[2,0] * this[3,1];
inverse[3,1] =
+ this[0,0] * this[2,1] * this[3,2]
+ this[0,1] * this[2,2] * this[3,0]
+ this[0,2] * this[2,0] * this[3,1]
- this[0,0] * this[2,2] * this[3,1]
- this[0,1] * this[2,0] * this[3,2]
- this[0,2] * this[2,1] * this[3,0];
inverse[3,2] =
+ this[0,0] * this[1,2] * this[3,1]
+ this[0,1] * this[1,0] * this[3,2]
+ this[0,2] * this[1,1] * this[3,0]
- this[0,0] * this[1,1] * this[3,2]
- this[0,1] * this[1,2] * this[3,0]
- this[0,2] * this[1,0] * this[3,1];
inverse[3,3] =
+ this[0,0] * this[1,1] * this[2,2]
+ this[0,1] * this[1,2] * this[2,0]
+ this[0,2] * this[1,0] * this[2,1]
- this[0,0] * this[1,2] * this[2,1]
- this[0,1] * this[1,0] * this[2,2]
- this[0,2] * this[1,1] * this[2,0];
double factor = (1.0 / determinant);
for (int i = 0; i < inverse.totalRows; i++)
{
for (int j = 0; j < inverse.totalColumns; j++)
{
inverse[i, j] *= factor;
}
}
return (inverse);
}
public MatrixF64 Inverse()
{
if
(
(this.totalRows <= 0)
|| (this.totalColumns <= 0)
|| (null == this.entries)
)
{
return (MatrixF64.Zero( 1, 1 )); // Matrix is empty.
}
if (this.totalRows != this.totalColumns)
{
// Matrix is not square
return (MatrixF64.Zero( 1, 1 ));
}
// Simple cases
if (1 == this.totalRows) { return (this.Inverse1x1()); }
if (2 == this.totalRows) { return (this.Inverse2x2()); }
if (3 == this.totalRows) { return (this.Inverse3x3()); }
if (4 == this.totalRows) { return (this.Inverse4x4()); }
int[] rowPermutationOfOriginalMatrix = null;
bool rowExchangeParityIsOdd = false;
MatrixF64 originalMatrix = new MatrixF64( this );
MatrixF64 LUFactorsMatrix = null;
MatrixF64.FindLUFactorsOfARowPermutedVersionOfAnOriginalMatrix
(
originalMatrix,
ref LUFactorsMatrix,
ref rowPermutationOfOriginalMatrix,
ref rowExchangeParityIsOdd
);
if ((null == LUFactorsMatrix)
|| (null == rowPermutationOfOriginalMatrix))
{
return (MatrixF64.Zero( this.totalRows, this.totalColumns ));
}
VectorF64 columnVector = VectorF64.Zero( totalRows );
VectorF64 solutionVector = VectorF64.Zero( totalRows );
MatrixF64 inverse =
MatrixF64.Zero( this.totalRows, this.totalColumns );
for (int j = 0; j < inverse.totalColumns; j++)
{
for (int i = 0; i < inverse.totalRows; i++)
{
columnVector[i] = 0.0;
}
columnVector[j] = 1.0;
MatrixF64.LUBacksubstitution
(
LUFactorsMatrix,
rowPermutationOfOriginalMatrix,
columnVector,
ref solutionVector
);
if (null != solutionVector)
{
for (int i = 0; i < inverse.totalRows; i++)
{
inverse[i, j] = solutionVector[i];
}
}
}
return (inverse);
}
// . . .
public static void Test()
{
// . . .
// Examples of matrix inverses:
MatrixF64 m2x2 =
new MatrixF64
(
2, 2,
2.0, 5.0, // row 0
-4.0, -7.0 // row 1
);
MatrixF64 inversem2x2 = m2x2.Inverse();
inversem2x2.WriteLine();
// [ -1.1666667, -0.83333333 ]
// [ 0.66666667, 0.33333333 ]
MatrixF64 prodinvm2x2andm2x2 = inversem2x2 * m2x2;
prodinvm2x2andm2x2.WriteLine();
// [ 1, 8.8817842e-16 ]
// [ 0, 1 ]
// This product is very close to a
// 2x2 identity matrix, as it should be.
MatrixF64 prodm2x2andinvm2x2 = m2x2 * inversem2x2;
prodm2x2andinvm2x2.WriteLine();
// [ 1, 0 ]
// [ 0, 1 ]
// This product is the 2x2 identity matrix,
// as it should be.
MatrixF64 m3x3 =
new MatrixF64
(
3, 3,
2.0, 5.0, 7.0, // row 0
-4.0, -1.0, 6.0, // row 1
9.0, 8.0, 3.0 // row 2
);
MatrixF64 inversem3x3 = m3x3.Inverse();
inversem3x3.WriteLine();
// [ -0.76119403, 0.6119403, 0.55223881 ]
// [ 0.98507463, -0.85074627, -0.59701493 ]
// [ -0.34328358, 0.43283582, 0.26865672 ]
MatrixF64 prodinvm3x3andm3x3 = inversem3x3 * m3x3;
prodinvm3x3andm3x3.WriteLine();
// [ 1, 0, 4.4408921e-16 ]
// [ 0, 1, 0 ]
// [ 0, 0, 1 ]
// This product is very close to a 3x3 identity matrix,
// as it should be.
MatrixF64 prodm3x3andinvm3x3 = m3x3 * inversem3x3;
prodm3x3andinvm3x3.WriteLine();
// [ 1, -4.4408921e-16, 4.4408921e-16 ]
// [ 0, 1, -2.220446e-16 ]
// [ -4.4408921e-16, 0, 1 ]
// This product is very close to a 3x3 identity matrix,
// as it should be.
// . . .
}
}
3.19 Multiplikative Produkt einer Matrix und ein Vektor
Die multiplikative Produkt einer Matrix (M) und (a) ein Vektor ist ein Vektor (b); (M)*(a) = (b).
Matrix-Vektor (M) „verwandelt“ (a) auf Vektor (b).
Der Code unten berechnet die multiplikative Produkt einer Matrix und einen Vektor.
public class MatrixF64
{
// . . .
public static VectorF64 operator *( MatrixF64 m, VectorF64 v )
{
if (null == m)
{
return (null); // Matrix is not specified.
}
if
(
(null == m.entries)
|| (m.totalRows <= 0)
|| (m.totalColumns <= 0)
)
{
return (null); // Matrix is empty.
}
if (null == v)
{
// Vector is not specified.
return (VectorF64.Zero( m.totalRows ));
}
if (v.Dimensions() != m.totalColumns)
{
// The number of columns of the matrix must be equal
// to the number of rows of the vector.
return (VectorF64.Zero( m.totalRows ));
}
// The result vector has the same number of rows as the matrix.
VectorF64 result = VectorF64.Zero( m.totalRows );
// Component (i) of the result vector is equal to the
// dot product of row (i) of the matrix with the vector.
for (int i = 0; i < m.totalRows; i++)
{
double dotProduct = 0.0;
for (int j = 0; j < m.totalColumns; j++)
{
dotProduct += (m[i, j] * v[j]);
}
result[i] = dotProduct;
}
return (result);
}
// . . .
public static void Test()
{
// . . .
// Examples of a product of a matrix and a vector:
MatrixF64 t3x3 =
new MatrixF64
(
3, 3,
2.0, 5.0, 7.0, // row 0
-4.0, -1.0, 6.0, // row 1
9.0, 8.0, 3.0 // row 2
);
VectorF64 p3x1 = new VectorF64( 1.0, 2.0, 3.0 );
VectorF64 q3x1 = t3x3 * p3x1;
q3x1.WriteLine();
// ( 33, 12, 34 )
// The multiplicative product of the inverse
// of the matrix and the result vector computed
// above should reproduce the original vector.
MatrixF64 inverset3x3 = t3x3.Inverse();
VectorF64 prodinvt3x3andq3x1 = inverset3x3 * q3x1;
prodinvt3x3andq3x1.WriteLine();
// ( 1, 2, 3 )
// This vector is equal to the original vector,
// as expected.
// . . .
}
}
3.20 Die Verwendung homogener Matrizen übersetzen homogene Vektoren
„Übersetzung:“ Eine Operation T(s), die durch einen bestimmten Vektor (s), dass Wandelt jeden Vektor (r) zu einer entsprechenden Vektor (r + s); T(s) * (r) = (r + s).
T(-s) ist die inverse von T(s); Inverse(T(s)) = T(-s); T(-s) * (r) = (r - s).
Eine Übersetzung Betrieb kann durch eine Matrix, so dass die Multiplikation der Übersetzung durch eine Matrix-Vektor hat zur Folge, dass die Übersetzung der Vektor.
Die Beiträge der Betrieb einer Übersetzung in die Komponenten des Ergebnisses Vektor nicht hängen direkt oder indirekt, über die Komponenten des ursprünglichen Vektors.
So ist eine Übersetzung Matrix müssen strukturiert in einer Weise, dass diese dazu beitragen, eine Übersetzung zu dem Ergebnis, ohne die davon betroffen sind, die direkt oder indirekt, indem die Komponenten des ursprünglichen Vektors.
Ein „homogener Vektor“ ist ein Vektor mit einer endgültigen Teilbetrag in Höhe von 1.
Eine „homogene Matrix“ ist eine Matrix mit einer abschließenden Zeile mit allen Einträgen gleich 0 außer für eine endgültige Eintragung 1.
Die multiplikative Produkt einer homogenen Matrix und anderen homogenen Matrix ist eine homogene Matrix.
Die multiplikative Produkt einer homogenen Matrix und eine homogene Vektor ist eine homogene Vektor.
Ein interessantes Merkmal dieses Produktes ist, dass die Einträge in der letzten Spalte der homogenen Matrix, mit Ausnahme der letzten Eintrag, sind direkt auf die Komponenten des Ergebnisses Vektor, ohne die davon betroffen sind, die direkt oder indirekt, indem die Komponenten der ursprünglichen Vektor.
So ist eine Übersetzung Betrieb kann durch eine homogene Matrix, und eine solche Matrix kann verwendet werden, zu übersetzen homogene Vektoren.
Wenn wir wollen, vertreten Positionen und Richtungen in (d)-dimensionalen Raum, und wir nutzen wollen Matrizen Positionen zu übersetzen, dann homogene Matrizen und Vektoren homogen verwendet werden kann.
Allerdings ist die homogene Vektoren müssen (d+1) Komponenten, und die homogene Matrizen müssen ((d+1)*(d+1)) Einträge.
Der letzte Komponente eines homogenen Vektor-und die endgültige Zeile eines homogenen Matrix sich nicht nur auf eine der (d) Koordinaten, die wir vertreten wollen in (d)-dimensionalen Raum, sondern dienen nur dazu, machen es möglich, Matrizen zu tun, einige Operationen unabhängig von der (d) koordinieren Werte.
Die Verwendung homogener Matrizen und Vektoren homogen macht es leichter, Compound-Transformationen und die Anwendung Transformationen zu Vektoren, aber auch andere Operationen werden mehr als unbequem.
Stellvertretend für Positionen oder Richtungen in (d)-dimensionalen Raum mit homogenen Vektoren bedeutet, dass die Vektoren haben (d+1) Komponenten, mit dem endgültigen Teilbetrag in Höhe von 1.
Berechnen der dot Produkt zweier Vektoren homogen mit einer regulären dot-Produkt-Funktion würde implizit die dot Produkt der ersten (d) Komponenten mit einem Wert von irrelevanten 1 aufgrund endgültig Komponenten 1.
Daher, um die dot Produkt der (d) relevanten Komponenten des homogenen Vektor (mit (d+1) Komponenten), den Punkt Produkt von homogenen Vektoren getan werden sollte, mit einem speziellen „homogenen Vektor dot Produkt-Funktion,“ die einfach überspringt die letzte Komponente der Vektoren.
Alternativ kann der Punkt Produkt könnte mit Hilfe des regulären dot Produkt-Funktion mit der Maßgabe, dass ein Betrag von 1 sollte von dem Ergebnis vor der Interpretation das Ergebnis als im Zusammenhang mit der (d) entsprechenden Koordinaten.
Der Code unten zeigt, wie eine Matrix gebildet werden kann, können dann verwendet werden, zu übersetzen, ein Vektor.
public class MatrixF64
{
// . . .
public static MatrixF64 FormHomogeneousTranslationMatrix( VectorF64 v )
{
// This method assumes the specified vector is a homogeneous
// vector, where the final component is not relevant to the
// translation. Thus, we form a square homogeneous matrix
// with a total number of rows equal to the number of
// components of the specified vector. We form an identity
// matrix of the required size, and copy all but the final
// component of the specified vector to the final column of
// the matrix.
if (null == v)
{
return (MatrixF64.Zero( 1, 1 )); // Vector is not specified.
}
if (v.Dimensions() <= 0)
{
return (MatrixF64.Zero( 1, 1 )); // Vector is empty.
}
MatrixF64 result =
MatrixF64.Identity( v.Dimensions() );
// Add all but the final component of the specified vector
// to the final column of the result matrix. The final
// entry of the final column of the matrix will remain one (1).
for (int i = 0; i < (result.totalRows - 1); i++)
{
result[i, (result.totalColumns - 1)] = v[i];
}
return (result);
}
// . . .
public static void Test()
{
// . . .
// Example of using a homogeneous matrix to
// translate a homogeneous vector:
// Forming a homogeneous matrix that represents
// a translation:
VectorF64 homogeneousTranslationVector4x1 =
new VectorF64( 10.0, 20.0, 30.0, 1.0 );
MatrixF64 translation4x4 =
MatrixF64.FormHomogeneousTranslationMatrix
( homogeneousTranslationVector4x1 );
translation4x4.WriteLine();
// [ 1, 0, 0, 10 ]
// [ 0, 1, 0, 20 ]
// [ 0, 0, 1, 30 ]
// [ 0, 0, 0, 1 ]
// Using the homogeneous matrix to translate
// a homogeneous vector:
VectorF64 homogeneousVector =
new VectorF64( 5.0, 6.0, 7.0, 1.0 );
VectorF64 translatedVector =
translation4x4 * homogeneousVector;
translatedVector.WriteLine();
// ( 15, 26, 37, 1 )
// The relevant vector components (5,6,7)
// were translated by (10,20,30) by the
// homogeneous translation matrix.
// . . .
}
}
3.21 (d)-dimensionalen Raum: Rotationen
„Rotation in (d)-dimensionalen Raum:“ Eine Operation (R) durch eine zweidimensionale Rotation (S) Flugzeug, ein Drehwinkel (t), und eine Rotation Mittelpunkt (c), dass wandelt beliebigen Punkt (p) zu einem entsprechenden Punkt (q), so dass:
(1) Length( q - c ) = Length( p - c ); die Entfernung zwischen dem neuen und dem Rotationszentrum Punkt ist die gleiche wie die Entfernung zwischen den Ausgangspunkt und Zentrum der Rotation.
Somit ist die neue Nummer befindet sich auf der Oberfläche eines bestimmten (d)-dimensionalen Raum;
(2) Lassen Sie (P) ein Flugzeug, das parallel zur Rotation Flugzeug (S) und enthält (p) Punkt.
Point (q) wird im Flugzeug (P).
Somit ist die neue Nummer befindet sich auf einem bestimmten zweidimensionalen Fläche in (d)-dimensionalen Raum;
(3) Dot( (q - c), (p - c) ) = Length( q - c ) * Length( p - c ) * Cos( t ); die dot Produkt des Vektors aus dem Rotationszentrum verweisen auf die ursprüngliche Nummer und den Vektor von der Rotation-Center auf die neue Nummer ist gleich dem Produkt aus den Längen der beiden Vektoren und Kosinus der Rotation Winkel.
Somit ist die neue Nummer befindet sich auf der Oberfläche eines bestimmten (d)-dimensionale Kegel;
Constraint (1) Grenzen der neuen Punkt auf der Oberfläche eines bestimmten (d)-dimensionalen Raum.
Zustand (2) weitere seinerseits die neue Nummer zu einem bestimmten zweidimensionalen Fläche in (d)-dimensionalen Raum.
Daher ist die neue Nummer muss in einer bestimmten zweidimensionalen Kreis in (d)-dimensionalen Raum.
Zustand (3) weitere seinerseits die neue Nummer zu einem bestimmten (d)-dimensionalen Kegel.
Daher ist die neue Nummer muss bei einem bestimmten Punkt (für Cos( t ) = 1 oder Cos( t ) = (-1)) oder in eine von zwei möglichen Punkten (für (-1) < Cos( t ) < 1).
Ein letztes Hindernis ist verpflichtet, wählen Sie eine der beiden möglichen Punkten, die durch die oben genannten Einschränkungen.
Lassen Sie T(s) ist ein „Übersetzungs-Betrieb,“ wo (s) ist eine willkürliche Vektor, so dass T(s) * (r) = (r + s) und Inverse(T(s)) * (r) = (r - s).
Eine Rotation R(S,t,c), für Flugzeug S, Winkel t und Mittelpunkt c, ausgedrückt werden kann im Hinblick auf die Übersetzung und eine Rotation mit einem Punkt im Zentrum der Herkunft: R(S,t,c) = T(c) * R(S,t,0) * T(-c) = T(c) * R(S,t,0) * Inverse(T(c)).
Lassen Sie R(S,t) stellt eine Drehung den Betrieb mit einer Rotation im Mittelpunkt der Ursprung der (d)-dimensionalen Raum.
Daher können alle beliebigen Rotation kann ausgedrückt werden als: R(S,t,c) = T(c) * R(S,t) * T(-c) = T(c) * R(S,t) * Inverse(T(c)).
Lassen Sie uns zwei-dimensionale Rotation (S) Flugzeuge, die die Herkunft der (d)-dimensionalen Raum und werden parallel auf zwei Achsen zu koordinieren.
Lassen Sie (a) und (b) werden Ganzzahlen, die Koordinatenachsen in (d)-dimensionalen Raum, so dass 0 <= a <= (d-1) und 0 <= b <= (d-1) und (a != b).
Wir definieren R(a,b,t) zu einer Drehung um die Herkunft der (d)-dimensionalen Raum, mit einer Drehung Ebene parallel zu Koordinatenachsen durch koordinieren Indizes (a) und (b), und mit einem Winkel (t), so dass R(a,b,(pi/2)) umwandeln würde einen positiven Wert zu koordinieren, das Koordinatensystem Achse durch (a) zu einem positiven koordinieren Wert für die Koordinierung der Achse durch (b); Beispiel: R(a,b,(pi/2)) * e(a) = e(b), wo e(k) zeigt eine Einheit Vektor entsprechende Grundlage zu koordinieren Achse (k).
Diese Definition stellt die endgültige Begriffsklärung notwendig, ermitteln die neue Nummer von einer Rotation.
Eine Rotation bewegt, dass Punkte entlang der Achse in Richtung a Achse b nach einem Winkel t ist äquivalent zu einer Rotation bewegt, dass Punkte entlang der Achse in Richtung b Achse a von einem Winkel (-t), dh R(a,b,t) = R(b,a,(-t)).
Wenn (d >= 2), dann (d)-dimensionalen Raum hat ((d*(d-1))/2) Paar Koordinatenachsen.
Das bedeutet, für (d >= 2), (d)-dimensionalen Raum hat ((d*(d-1))/2) verschiedene Achsen-Rotation Flugzeuge ausgerichtet.
(Beispiele: d=2: () x-y Flugzeug; d=3: (x-y Flugzeug, y-z Flugzeug, Flugzeug z-x); d=4: (x-y Flugzeug, y-z Flugzeug, z-x Flugzeug, x-w Flugzeug, z-w Flugzeug, w-y Flugzeug).)
Der Code unten zeigt, wie eine Matrix kann verwendet werden, um die Rotation R(a,b,t).
Ein Beispiel wird gezeigt, dass eine Rotation verwendet Matrix in eine homogene Weise zu drehen, eine homogene Vektor zu einem neuen homogenen Vektor.
public class MatrixF64
{
// . . .
public static MatrixF64 Rab( int rows, int a, int b, double angle )
{
if (rows <= 0)
{
// Invalid row count specified.
return (MatrixF64.Zero( 1, 1 ));
}
MatrixF64 result = MatrixF64.Identity( rows );
// If (a==b) (rotate coordinate (a) to (a)), then
// there is no rotation to do.
if ((a != b) && (a >= 0) && (a < rows) && (b >= 0) && (b < rows))
{
double sine = Math.Sin( angle );
double cosine = Math.Cos( angle );
// Override m[a,a] = cosine; m[a,b] = -sine;
// m[b,a] = sine; m[b,b] = cosine;
result[a, a] = cosine;
result[b, b] = cosine;
result[a, b] = (-(sine));
result[b, a] = sine;
}
return (result);
}
public static MatrixF64 Rabk( int rows, int a, int b, int k )
{
double angle = (Math.PI / 2.0) * (double)k;
return (MatrixF64.Rab( rows, a, b, angle ));
}
// . . .
public static void Test()
{
// . . .
// Example of using a homogeneous matrix
// to rotate a homogeneous vector:
// Forming a homogeneous rotation matrix that
// rotates a positive component along coordinate
// axis 0 to a positive component along
// cooridnate axis 1, in 3-dimensional space.
// (The homogeneous matrix must be (3+1)=4
// rows by (3+1)=4 columns.)
MatrixF64 rotation4x4 =
MatrixF64.Rab( 4, 0, 1, (0.5 * Math.PI) );
rotation4x4.WriteLine(3);
// [ 6.12e-17, -1, 0, 0 ]
// [ 1, 6.12e-17, 0, 0 ]
// [ 0, 0, 1, 0 ]
// [ 0, 0, 0, 1 ]
// (The entries '6.12e-17' are negligible.)
// The upper-left 3x3 part of the matrix represents
// a counterclockwise rotation of a quarter-turn
// such that a positive component along axis 0
// would be rotated to a positive component along
// axis 1, in 3-dimensional space.
// Using the homogeneous rotation matrix to
// rotate a homogeneous vector:
VectorF64 homogeneousVector2 =
new VectorF64( 5.0, 6.0, 7.0, 1.0 );
VectorF64 rotatedVector =
rotation4x4 * homogeneousVector2;
rotatedVector.WriteLine();
// ( -6, 5, 7, 1 )
// The relevant vector components (5,6,7) were
// rotated to (-6,5,7), which is consistent with
// a counterclockwise quarter-turn rotation
// with a rotation plane parallel to axes 0 and 1.
// . . .
}
}
3.22 Vorgeschlagene Definition von „gegen“
In zwei-dimensionalen Raum, der wiederum ein Viertel-Drehung umwandeln kann, dass der Vektor e(0) zu e(1) ist entgegen der Auffassung.
Die Rotation ist: R(0,1,(pi/2)) * e(0) = e(1).
Daher ist die Drehung im Gegenuhrzeigersinn R(0,1,t) ist.
In drei-dimensionalen Raum, der wiederum ein Viertel-Drehung umwandeln kann, dass der Vektor e(0) zu e(1) ist entgegen der Auffassung.
Das Viertel-Drehung drehen, dass umwandeln kann den Vektor e(1) zu e(2) gilt auch als entgegen.
Das Viertel-Drehung drehen, dass umwandeln kann den Vektor e(2) zu e(0) gilt auch als entgegen.
Daher ist die Drehungen im Gegenuhrzeigersinn { R(0,1,t), R(1,2,t), R(2,0,t) } sind.
Die Booleschen Formel passt unter die Definition des Begriffs "entgegen zu zweidimensionalen und dreidimensionalen Raum, und extrapoliert das Muster für den allgemeinen Fall der Rotation R(a,b,t):
bool counterClockwise =
(
( (a < b) && (((a + b) % 2) == 1) )
|| ( (a > b) && (((a + b) % 2) == 0) )
);
Diese Regel würde definieren, die unten wie Drehungen „im Gegenuhrzeigersinn:“
R(0,1,t),
R(2,0,t), R(1,2,t),
R(0,3,t), R(3,1,t), R(2,3,t),
R(4,0,t), R(1,4,t), R(4,2,t), R(3,4,t),
R(0,5,t), R(5,1,t), R(2,5,t), R(5,3,t), R(4,5,t),
...
3.23 (d)-dimensionalen Raum: Drehungen im Uhrzeigersinn
Mit dem vorgeschlagenen Definition der „gegen den“ im vorherigen Abschnitt, der unter Drehungen sind „im Uhrzeigersinn:“
R(1,0,t),
R(0,2,t), R(2,1,t),
R(3,0,t), R(1,3,t), R(3,2,t),
R(0,4,t), R(4,1,t), R(2,4,t), R(4,3,t),
R(5,0,t), R(1,5,t), R(5,2,t), R(3,5,t), R(5,4,t),
...
Zum Beispiel, in vier-dimensionalen Raum, die sechs Rotation Matrizen unten vertreten Drehungen im Uhrzeigersinn:
R(1,0,t) = Cos(t), Sin(t), 0, 0,
-Sin(t), Cos(t), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
R(0,2,t) = Cos(t), 0, -Sin(t), 0,
0, 1, 0, 0,
Sin(t), 0, Cos(t), 0,
0, 0, 0, 1
R(2,1,t) = 1, 0, 0, 0,
0, Cos(t), Sin(t), 0,
0, -Sin(t), Cos(t), 0,
0, 0, 0, 1
R(3,0,t) = Cos(t), 0, 0, Sin(t),
0, 1, 0, 0,
0, 0, 1, 0,
-Sin(t), 0, 0, Cos(t)
R(1,3,t) = 1, 0, 0, 0,
0, Cos(t), 0, -Sin(t),
0, 0, 1, 0,
0, Sin(t), 0, Cos(t)
R(3,2,t) = 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, Cos(t), Sin(t),
0, 0, -Sin(t), Cos(t)
3.24 (d)-dimensionalen Raum: Gesamtzahl der Achse ausgerichtet Orientierungen
Die Anzahl der unterschiedlichen Achse ausgerichtet Orientierungen in (d)-dimensionalen Raum ist:
distinctOrientations = Factorial( d ) * IntPow( 2, (d-1) );
Ein Argument zur Unterstützung dieser Formel wird in den Absätzen weiter unten.
Experimentelle Überprüfung der Belege Formel ist ebenfalls dargestellt.
Lassen Sie { g(0), g(1), ..., g(d-1) } werden eine Reihe von (d) gegenseitig-senkrecht Einheitsvektoren an ein Objekt in (d)-dimensionalen Raum.
Diese Einheitsvektoren zeigen die Ausrichtung des Objekts in Bezug auf die Koordinatenachsen des (d)-dimensionalen Raum.
Lassen Sie den „ursprünglichen Ausrichtung“ des Objekts werden, so dass g(i) = e(i), für alle 0 <= i <= (d-1); jeder Vektor Ausrichtung des Objekts ist mit einer getrennten Einheit Vektor Grundlage eines Koordinatensystem im (d)-dimensionalen Raum.
Die erste Orientierung Vektor des Objekts kann parallel zu einem der (d) Achsen der (d)-dimensionalen Raum, und lassen sich in einem der beiden Richtungen im Verhältnis zu dem Achse.
Somit ist die erste Orientierung Vektor hat (2 * d) Optionen.
Die zweite Ausrichtung der Vektor-Objekt kann parallel zu einem der (d-1) verbleibenden Achsen, mit zwei möglichen Richtungen bezogen auf diese Achse.
Somit ist die zweite Ausrichtung Vektor hat (2 * (d-1)) Optionen.
Auf die gleiche Weise, die nächste Ausrichtung Vektor hat (2 * (d-2)) Optionen.
Die endgültige Orientierung Vektor ist gezwungen, die letzten verbleibenden Achse und wird gezwungen, eine der beiden möglichen Richtungen entlang dieser Achse (zur Erhaltung der Parität der Satz von Vektoren Orientierung, die Determinante für eine geordnete Men