Γραμμική Άλγεβρα
Colin Fahey
1. Λογισμικό
LinearAlgebra.zip
Γραμμική άλγεβρα πηγαίο κώδικα (C#)
19910 bytes
MD5: 11d8c8035cac30ba543e5e0b72ee9767
2. Εισαγωγή
Αυτό το άρθρο περιγράφει διανύσματα και πίνακες σε (d) διαστάσεων χώρο.
3. (d) διαστάσεων χώρο: χαρακτηριστικά
3.1 Array
"array:" Μια συλλογή από μεταβλητές τέτοιες ώστε κάθε μεταβλητή έχει ένα μοναδικό όνομα, και τα ονόματα αυτών που μπορεί να καταταχθεί σε κάποια τάξη.
Ακέραιος τιμές μπορούν να χρησιμοποιηθούν ως ονόματα για τις μεταβλητές σε έναν πίνακα.
Για παράδειγμα, εάν μια σειρά περιέχει (d) μεταβλητές, τότε ο ακέραιος { 0, 1, 2, ..., (d-1) } τιμές μπορεί να είναι το ονόματα που αποδίδονται στις μεταβλητές της συστοιχίας.
3.2 (d) διαστάσεων φορέα
"(d) διαστάσεων φορέα:" Μια σειρά από μεταβλητές (d).
"διανυσματική συνιστώσα:" Μια μεταβλητή μέσα σε ένα φορέα.
3.3 (d) διαστάσεων διανυσματικό χώρο
"μονοδιάστατη διάστημα:" Το πλήρες σύνολο των αξιών που μπορούν να αποθηκευτούν από ένα μεταβλητό.
"(d) διαστάσεων χώρο:" Η πλήρης σειρά των συνδυασμών των αξιών που μπορούν να αποθηκευτούν από μια σειρά από μεταβλητές (d).
Ο επίσημος ορισμός ενός "φορέα διάστημα:"
Ας (T) να είναι ένας βασικός τύπος (παραδείγματα: πραγματικός αριθμός, ακέραιος, αριθμός πολύπλοκων, ορθολογική αριθμό, κλπ).
Κάθε μεταβλητή του βασικού τύπου ονομάζεται "μονοδιάστατη."
Μια "(T) τύπου (d) διαστάσεων του φορέα" είναι ο "χώρος" που (S) των (d) διαστάσεων φορέων έχοντας δύο επιχειρήσεων, φορέων (+) Επιπλέον, τον πολλαπλασιασμό και τη μονοδιάστατη (*), οι οποίοι πληρούν τις παρακάτω προϋποθέσεις:
(1) Εάν (v) και (w) οι δύο φορείς σε (S), τότε (v + w) είναι επίσης ένα φορέα στον (S)?
(2) Εάν (u), (v), και (w) είναι τριών φορέων σε (S), τότε (u + v) + w = u + (v + w)?
[ύλης commutativity]
(3) Εάν (v) και (w) οι δύο φορείς σε (S), τότε (v + w) = (w + v)?
[ύλης associativity]
(4) Υπάρχει ένα "μηδενικό φορέα," (0), (S), τέτοια ώστε για κάθε φορέα (v) στο (S), (v + (0)) = v?
[ύλης ταυτότητα]
(5) Αν τυχόν (c) είναι μονοδιάστατη (T) του τύπου, και (v) είναι κάθε φορέα στο (S), τότε το προϊόν (c * v) αποτελεί φορέα στον (S)?
(6) Εάν (a), (b), και (c) είναι κάθε scalars του τύπου (T), και (v) και (w) είναι φορείς σε κάθε (S), τότε (a + b) * v = a*v + b*v, και c*(v + w) = c*v + c*w?
[multiplicative distributivity]
(7) Εάν (a) και (b) είναι κάθε scalars του τύπου (T), και (v) είναι κάθε φορέα στο (S), τότε (a*b)*v = a*(b*v)?
(8) Αν "1" είναι μια μονοδιάστατη του τύπου (T) τέτοια που (1*1)=1, και (v) είναι κάθε φορέα στο (S), τότε (1*v) = v?
(9) Για κάθε φορέα (v) στο (S), τον φορέα (-1)*v = -v πληροί v + (-v) = (0)?
[προσθετικό αντίστροφο]
3.4 (d) διαστάσεων φορέα κωδικός
Ο κώδικας που ακολουθεί δείχνει πώς μια (d) διαστάσεων φορέα, με 64-bit floating-point συστατικά, μπορούν να εφαρμοστούν.
Ένας array είναι αρκετή για να αποτελέσει φορέα.
Ο κώδικας έχει κάτω από ένα array που περιέχονται σε μια τάξη, μόνο για λόγους ευκολίας.
Ο κωδικός αυτός δεν είναι αποτελεσματική.
Μια δομή (π.χ.: "struct", μια τιμή τύπου) που εκπροσωπούν φορείς του σταθερού αριθμού των διαστάσεων (παραδείγματα: 3 ή 4) είναι πιθανό να είναι πολύ πιο αποτελεσματική από τη γενική (d) διαστάσεων φορέα κατηγορία εμφανίζεται εδώ.
Παρόλο που ο κώδικας ορίζει ένα φορέα κάτω με floating-point συνιστώσες, το έγγραφο αυτό, επίσης, κάνει χρήση των φορέων με ακέραιος συστατικά.
Οι παρακάτω κώδικα μπορούν εύκολα να τροποποιηθούν για να υλοποιήσουν φορείς με ακέραιος συστατικά.
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) διαστάσεων μηδέν φορέα:" Ένας φορέας με όλες τις συνιστώσες (d) ίση με μηδέν.
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) διαστάσεων χώρο: σημεία
"(d) διαστάσεων διαστημικό σημείο:" Μια σειρά από μεταβλητές (d) με ειδικές τιμές "(τιμές συντονίσουν)."
"(d) διαστάσεων χώρο προέλευσης:" Μια σειρά από μεταβλητές (d) με όλες τις τιμές ίσες με το μηδέν.
3.6 (d) διαστάσεων φορέων: μη σχετικό και σχετική
Μια (d) διαστάσεων φορέα που "δεν" είναι "σχετική" είναι μια (d) διαστάσεων φορέων που άμεσα ή αντιπροσωπεύει ένα καθεστώς ρύθμισης.
Μια (d) διαστάσεων φορέα που είναι "σχετικό" είναι ένα (d) διαστάσεων φορέα που εκπροσωπεί τις αλλαγές σε ένα σύνολο στοιχείων.
Ένα σχετικό φορέα μπορεί να αντιπροσωπεύει τη διαφορά μεταξύ δύο μη σχετικούς φορείς.
Λαμβάνοντας υπόψη ένα σχετικό φορέα, για τον καθορισμό ενός μη σχετική ιδιότητα ή την ρύθμιση της χρησιμοποίησης του εν λόγω φορέα απαιτεί σχετική συνδυάζει τη σχετική με φορέα μη σχετικό φορέα.
Μη σχετική φορείς σχετικούς φορείς και οι δύο φορείς.
Είτε ένα συγκεκριμένο φορέα είναι μη συγγενής ή σχετική πρέπει να προσδιορίζεται κατά τις οποίες ο φορέας έχει οριστεί.
Εάν ένα (d) διαστάσεων φορέας ερμηνεύεται ως μη σχετική, τότε η (d) διαστάσεων φορέα μπορεί να αντιπροσωπεύει ένα σημείο στο (d) διαστάσεων χώρο.
3.7 (d) διαστάσεων φορέα Επιπλέον, η αφαίρεση, και την κλιμάκωση
Vector Επιπλέον, η αφαίρεση, και την κλιμάκωση:
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) διαστάσεων βάση διανυσμάτων
Ο επίσημος ορισμός της "βάσης" του φορέα διάστημα:
Ας (T) να είναι ένας βασικός τύπος (παραδείγματα: πραγματικός αριθμός, ακέραιος, αριθμός πολύπλοκων, ορθολογική αριθμό, κλπ).
Κάθε μεταβλητή του βασικού τύπου ονομάζεται "μονοδιάστατη."
Ας (V) είναι "(T) τύπου (d) διαστάσεων διανυσματικό χώρο."
Εάν η μη μηδενική φορέων { u1, u2, ..., ud } στο (V) είναι τέτοια ώστε κάθε φορέας (v) στο (V) μπορεί να γραφτεί ως ένα "γραμμικό συνδυασμό" των εν λόγω φορέων, v = c1*u1 + c2*u2 + ... + cd*ud, όπου { c1, c2, ..., cd } είναι scalars του τύπου (T), τότε είναι (V) "βαθμονομείται" με φορείς { u1, u2, ..., ud }.
Οποιοδήποτε σύνολο των μη μηδενικών διανυσμάτων { u1, u2, ..., ud } που "εκτείνονται σε" διανυσματικό χώρο (V) ονομάζεται "βάση" (V).
Μια απλή "βάση" μια (d) διαστάσεων διανυσματικό χώρο είναι το σύνολο των (d) διακριτή (d) διαστάσεων φορείς, με κάθε ένα στοιχείο ίσο με ένα και όλα τα άλλα στοιχεία που ισούται με μηδέν.
Οι εν λόγω φορείς είναι "orthonormal" βάση, σημαίνει ότι έχουν αμοιβαία κάθετα "(ορθογώνια)" και ότι κάθε φορέας έχει μονάδα μήκους.
Κάθε τέτοιος φορέας είναι φορέας μιας μονάδας παράλληλη προς μια από τις (d) συντονίζουν άξονες.
Εκφράζοντας μια αυθαίρετη φορέα ως ένα "γραμμικό συνδυασμό" αυτών των φορέων είναι η άμεση βάση? Κάθε στοιχείο του αυθαίρετου φορέα πολλαπλασιάζεται με την αντίστοιχη βάση του φορέα, καθώς και τα προϊόντα αυτά να προστεθούν μαζί για να σχηματίσουν την αυθαίρετη φορέα.
Ο κώδικας που ακολουθεί δείχνει πώς ένας φορέας μπορεί να εκφραστεί ως ένα "γραμμικό συνδυασμό" των φορέων βάση.
Οι παρακάτω κώδικα αυθαίρετα ορίζει ως βάση orthonormal σειρά φορέων.
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 )
// . . .
}
}
Κάθε φορέας στον (d) διαστάσεων χώρος μπορεί να εκφραστεί ως άθροισμα των προϊόντων βάση των αριθμών και των φορέων:
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) διαστάσεων χώρο: την απόσταση μεταξύ των σημείων
Ας (P) είναι (d) διαστάσεων φορέα που αντιπροσωπεύει ένα σημείο στο (d) διαστάσεων χώρο.
Ας (Q) είναι (d) διαστάσεων φορέα που αντιπροσωπεύει ένα σημείο στο (d) διαστάσεων χώρο.
Ας (R) είναι (d) διαστάσεων φορέα που αντιπροσωπεύει τη μεταβολή (d) συντεταγμένες για να φτάσετε από το σημείο (P) σε σημείο (Q)? R = (Q - P).
Σε ένα χώρο διαστάσεων, P = ( p0 ), Q = ( q0 ), και R = (Q - P) = ( q0 - p0 ).
Η απόσταση μεταξύ των δύο σημείων είναι: Abs( q0 - p0 ).
Σε δισδιάστατο χώρο, P = ( p0, p1 ), Q = ( q0, q1 ), και R = (Q - P) = ( q0-p0, q1-p1 ).
Ερμηνεύοντας τις δύο κάθετες μετατοπίσεις όπως το κάθετο πλευρές του τριγώνου δεξιά, η απόσταση μεταξύ των σημείων αντιστοιχεί στο μήκος του hypotenuse αυτού του τριγώνου.
Η Pythagorean τύπος, (a*a) + (b*b) = (c*c), όπου (a) και (b) είναι το μήκος της καθέτου πλευρές ενός τριγώνου δεξιά, και (c) είναι το μήκος του hypotenuse (skew πλευρά), μπορεί να χρησιμοποιηθεί για να καθοριστεί η απόσταση μεταξύ των δύο σημείων: Sqrt( Sq(q0-p0) + Sq(q1-p1) ).
Στο τρισδιάστατο χώρο, P = ( p0, p1, p2 ), Q = ( q0, q1, q2 ), και R = (Q - P) = ( q0-p0, q1-p1, q2-p2 ).
Ερμηνεύοντας τον εκτοπισμό ( q0-p0, q1-p1, 0 ) όπως το κάθετο πλευρές του τριγώνου δεξιά, και χρησιμοποιώντας τον τύπο Pythagorean, δίνει η απόσταση μεταξύ του σημείου (P) και το σημείο ( q0, q1, p2 ): d01 = Sqrt( Sq(q0-p0) + Sq(q1-p1) ).
Η μετατόπιση ( q0-p0, q1-p1, 0 ) είναι κάθετος προς τον εκτοπισμό ( 0, 0, q2-p2 ), και άλλο δικαίωμα τρίγωνο μπορεί να σχηματιστεί και η Pythagorean τύπος μπορεί να χρησιμοποιηθεί ξανά.
Έτσι, η απόσταση από το σημείο (P) σε σημείο (Q) δίδεται από τη σχέση: 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) ).
Η μέθοδος της επέκτασης της απόστασης τύπος από δισδιάστατο χώρο σε τρισδιάστατο χώρο μπορεί να εφαρμοστεί κατ 'επανάληψη για να καθορίσει τελικά την απόσταση φόρμουλα για (d) διαστάσεων χώρο: Sqrt( (Sq(q0-p0) + Sq(q1-p1)) + Sq(q2-p2) + ... + Sq(qd-pd) ).
Οι παρακάτω κώδικα ορίζει μια λειτουργία που ονομάζεται "Μήκος" οποία υπολογίζει το μήκος ενός (d) διαστάσεων φορέα.
Όταν ένας φορέας εκπροσωπεί τη μετατόπιση μεταξύ δύο σημείων στο χώρο (d) διαστάσεων, το μήκος του φορέα που εκπροσωπεί την απόσταση μεταξύ των δύο αυτών σημείων.
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) διαστάσεων φορέων: dot προϊόντος
Το "προϊόν dot" μετατρέπει (d) δυο διαστάσεων σε μια σειρά φορέων.
Ο κώδικας παρακάτω υπολογίζει μια κουκίδα προϊόν των δύο φορέων:
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);
}
// . . .
}
Για κάθε φορέα, (A), Length(A) = Sqrt(Dot(A,A)).
3.11 (d) διαστάσεων φορέων: ορισμός της "παράλληλης"
Διανύσματα (A) και (B) είναι "παράλληλες," αν όλα τα ακόλουθα είναι αληθές:
(1) A.Length() > 0?
(2) B.Length() > 0?
(3) Abs(Dot(A,B)) = A.Length()*B.Length().
Οι παρακάτω κώδικα καθορίζει εάν ένα ζεύγος διανυσμάτων είναι παράλληλες (πιθανώς αντι-ευθυγραμμισμένο).
Floating-point αριθμών μπορούν να συσσωρεύουν λάθη στο κλασματικό μέρος, λόγω της περιορισμένης ακρίβειας, και, ως εκ τούτου, θα πρέπει να περιλαμβάνουν κώδικα υπολογιστή μη μηδενικές ανοχές κατά τη σύγκριση floating-point αριθμών.
Ο κώδικας περιλαμβάνει π.χ. τις αξίες της ανοχής, αλλά το παράδειγμα αξίες της ανοχής μπορεί να μην είναι κατάλληλο για ορισμένα καθήκοντα.
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) διαστάσεων φορέων: ορισμός της "καθέτου"
Διανύσματα (A) και (B) είναι "κάθετα" αν όλα τα ακόλουθα είναι αληθές:
(1) A.Length() > 0?
(2) B.Length() > 0?
(3) Abs(Dot(A,B)) = 0.
Οι παρακάτω κώδικα καθορίζει εάν ένα ζεύγος φορέων είναι κάθετος. Floating-point αριθμών μπορούν να συσσωρεύουν λάθη στο κλασματικό μέρος, λόγω της περιορισμένης ακρίβειας, και, ως εκ τούτου, θα πρέπει να περιλαμβάνουν κώδικα υπολογιστή μη μηδενικές ανοχές κατά τη σύγκριση floating-point αριθμών.
Ο κώδικας περιλαμβάνει π.χ. τις αξίες της ανοχής, αλλά το παράδειγμα αξίες της ανοχής μπορεί να μην είναι κατάλληλο για ορισμένα καθήκοντα.
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 Πίνακες
"matrix:" Μια συλλογή από μεταβλητές τέτοιες ώστε κάθε μεταβλητή έχει ένα μοναδικό συνδυασμό μιας "σειράς" όνομα και ένα όνομα "στήλης."
"entry:" Μια μεταβλητή μέσα σε μήτρα.
Ακέραιος αξιών μπορεί να χρησιμοποιηθεί ως "γραμμή" ονόματα και "στήλη" ονόματα για τις μεταβλητές μέσα σε μια μήτρα.
Για παράδειγμα, εάν μια μήτρα έχει (totalRows) σειρές και (totalColumns) στήλες, τότε ο ακέραιος { 0, 1, ..., (totalRows-1) } τιμές μπορεί να είναι το ονόματα που αποδίδονται στις σειρές, ο ακέραιος και αξίες { 0, 1, ..., (totalColumns-1) } μπορεί να είναι τα ονόματα που αποδίδονται στις στήλες.
Έτσι, μια μεταβλητές σε μια μήτρα, μπορεί να ορίζεται με την ένδειξη ενός ζεύγους ακέραιοι, ( row, column ), αναφέρει ο συνδυασμός της σειράς και στήλης δείκτες που αντιστοιχούν σε ειδικές μεταβλητή.
Το μέγεθος της μήτρας είναι καθορισμένη ως "(totalRows) * (totalColumns)" (ή "(totalRows) από (totalColumns))."
Αυτή η σειρά των διαστάσεων είναι το ίδιο με τη σειρά του τις διαστάσεις που χρησιμοποιούνται για να προσδιορίσουν τις εγγραφές στο μητρώο "(( row, column ))."
[Η παρούσα σύμβαση είναι κάπως ατυχής, διότι για πολλούς δισδιάστατο χρήσεις (παραδείγματα: εικόνες, γραφήματα, κλπ) τα συνήθη σύμβαση έχει ως στόχο να προσδιορίσει τις διαστάσεις, όπως και "width * height" συντεταγμένες ως "( horizontal, vertical )" (ή "( x, y ))."
Αυτό είναι το αντίθετο της τάξης του διαστάσεις και συντεταγμένες χρησιμοποιούνται για την περιγραφή των πινάκων και οι καταχωρίσεις τους. ]
Μια μήτρα με (totalRows) ίση με (totalColumns) ονομάζεται "πλατεία?" Άλλως, η μήτρα ονομάζεται "ορθογώνιος."
Μια μήτρα μπορεί να θεωρηθεί ότι περιέχει μια σειρά από "σειρά φορέων," όπου οι μεταβλητές σε κάθε σειρά έχουν ερμηνευθεί ότι ανήκουν σε φορέα.
Μια μήτρα επίσης μπορεί να θεωρηθεί ότι περιέχει μια σειρά από "φορείς στήλη," όπου οι μεταβλητές σε κάθε στήλη είναι ερμηνευθεί ότι ανήκουν σε φορέα.
Πίνακες μπορούν να αντιπροσωπεύουν ένα ευρύ φάσμα των μαθηματικών σχέσεων.
Η έννοια της μήτρας, καθώς και οι πράξεις που θα μπορούσαν να είναι κατάλληλο για επεξεργασία ενός μήτρας καταχωρήσεις, εξαρτάται από το πλαίσιο.
Ωστόσο, υπάρχουν βασικοί κανόνες της αριθμητικής μήτρα που είναι σχετικές με πολλές καταστάσεις, και οι εν λόγω βασικοί κανόνες που θα καθοριστούν στο επόμενο τμήμα.
Ένας array και ένα (totalColumns) αξία αρκούν για να εκπροσωπούν μια μήτρα.
Η συστοιχία μπορεί να έχει (totalRows * totalColumns) μεταβλητές, και η είσοδος στα ( row, column ) μπορεί να ανταποκρίνεται στον πίνακα μεταβλητών στο δείκτη ((totalColumns * row) + column).
Ο κώδικας ορίζει παρακάτω πίνακα, με 64-bit floating-point εγγραφές.
Ένας array και ένα (totalColumns) αξία αρκούν για να εκπροσωπούν μια μήτρα.
Οι παρακάτω κώδικα έχει μια σειρά και μια (totalColumns) αξία που περιέχονται σε μια τάξη, μόνο για λόγους ευκολίας.
Οι παρακάτω κώδικα δεν προορίζεται να είναι και αποτελεσματική.
Μια δομή (π.χ.: "struct", μια τιμή τύπου) που αντιπροσωπεύουν matrices ιδιαίτερα σταθερό διαστάσεις (παραδείγματα: 2*2, 3*3, ή 4*4) είναι πιθανό να είναι πολύ πιο αποτελεσματική από τη γενική κατηγορία (totalRows * totalColumns) εμφανίζεται εδώ.
Παρόλο που ο κώδικας ορίζει παρακάτω πίνακα με floating-point καταχωρήσεις, το άρθρο αυτό επιτρέπει επίσης τη χρήση των πινάκων με ακέραιος εγγραφές.
Οι παρακάτω κώδικα μπορεί εύκολα να τροποποιηθεί για να εφαρμοστούν οι πίνακες με εγγραφές ακέραιος.
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
// . . .
}
}
"μηδέν μήτρας:" μια μήτρα με όλες τις ενδείξεις που ισούται με το μηδέν.
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 Επιπλέον, η αφαίρεση, πολλαπλασιασμός και
Matrix πρόσθεσης, της αφαίρεσης και του πολλαπλασιασμού.
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 ]
// . . .
}
}
"ταυτότητα μήτρα:" Μια πλατεία μήτρας (ισοδυναμεί με συνολικό σειρές συνολικά στήλες) με καταχωρήσεις ίση με (1) σχετικά με τη διαγώνια (καταχωρήσεις με ένα στίχο δείκτη ίσο με τη στήλη δείκτη), και με όλες τις άλλες εισόδους ίση με (0).
Εάν ένα τετραγωνικό μήτρα, (M), πολλαπλασιάζεται με μια "ταυτότητα μήτρα," (I), του ίδιου αριθμού γραμμών (και στήλες), το προϊόν είναι ίσο με (M).
Πολλαπλασιάζοντας (I) από (M) επίσης παράγει ένα προϊόν ίσο με (M).
Έτσι, η "ταυτότητα μήτρα" είναι ανάλογη με τον αριθμό "1" για τον πολλαπλασιασμό των αριθμών (scalars).
Οι παρακάτω κώδικα δημιουργεί μια ταυτότητα μήτρα με καθορισμένο αριθμό γραμμών.
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? Back-υποκατάστασης
"Matrix LU factoring:" Μια διαδικασία η οποία μετατρέπει ένα τετράγωνο μήτρα, (M), σε δύο νέα πλατεία μήτρες, (L) και (U), έχοντας το ίδιο μέγεθος με (M), τέτοια που (L) * (U) = (M), και ότι τέτοια μήτρα (U) είναι "άνω τριγωνικός" (όλες οι ενδείξεις είναι κάτω από την διαγώνιο μηδέν), και η μήτρα είναι "χαμηλότερη" (L) "τριγωνική" (όλες οι ενδείξεις πάνω από την διαγώνιο είναι μηδέν).
Matrix LU factoring μπορεί να χρησιμοποιηθεί ως μέρος μιας ευρύτερης διαδικασίας για την επίλυση ενός συστήματος εξισώσεων, ή να βρεθεί η αντίστροφη μιας μήτρας, ή για να βρείτε το καθοριστικό της μήτρας.
Matrix LU factoring αποτελεί εναλλακτική λύση για την εξάλειψη "Gauss-Jordan διαδικασία.
Gauss-Jordan εξάλειψή της απαιτεί ένα σύστημα εξισώσεων (A)*(x)=(b), ενώ LU factoring απαιτεί μόνο μια μήτρα (A).
Επίσης, μετά τον καθορισμό του LU factoring (A) της μήτρας, είναι πολύ εύκολο να διαπιστωθεί (x) δοθεί οποιαδήποτε (b).
Η διαδικασία για να λύσουμε το φορέα (x) στο (A)*(x)=(b), δεδομένου (b) και την LU factoring (L)*(U)=(A), προϋποθέτει μια διαδικασία η οποία θα ονομαστεί "back-υποκατάσταση" εδώ.
Οι παρακάτω κώδικα περιλαμβάνει μια διαδικασία για τον υπολογισμό της LU παράγοντες της κάθε πλατεία, μη ενικό μήτρα.
Οι παρακάτω κώδικα περιλαμβάνει μια διαδικασία για να κάνουμε πίσω LU-υποκατάστασης.
Προσοχή: Ο υπολογιστής υπολογίζει τις παρακάτω κώδικα L και U παράγοντες της σειράς-permuted μια συγκεκριμένη εκδοχή της μήτρας.
Επίσης, το L και U μήτρα αποτελέσματα συνδυάζονται σε μία ενιαία μήτρα παραγωγής.
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: καθοριστικός
"Καθοριστικός:" Μια λειτουργία που μετατρέπει κάθε τετραγωνικό (n * n) μήτρα (A) σε μια σειρά, det(A), τέτοια ώστε:
(1) Εάν μια μήτρα (B) αποτελέσματα από την ανταλλαγή των δύο σειρές, ή δύο στήλες, μια μήτρα (A), τότε det(B) = (-(det(A)))?
(2) Εάν μια μήτρα (B) προκύπτει από τον πολλαπλασιασμό κάθε σειράς, ή οποιαδήποτε στήλη, μια μήτρα (A), από μια σειρά (c), τότε det(B) = c * det(A)?
(3) Εάν μια μήτρα (B) προκύπτει από την προσθήκη ενός πολλαπλασίου της μία γραμμή στην άλλη γραμμή, ή από την προσθήκη ενός πολλαπλασίου της μία στήλη στην άλλη στήλη, τότε det(B) = det(A).
(4) Εάν (A) είναι ένα επάνω τριγωνική μήτρα (A[i,j]=0 για όλους (i>j)) ή μια μικρότερη-τριγωνική μήτρα (A[i,j]=0 για όλους (i<j)), τότε det(A) = (A[0,0] * A[1,1] * ... * A[n-1,n-1])?
(Για παράδειγμα, το καθοριστικό στοιχείο της ταυτότητας μήτρα είναι ένας? det(I) = 1.)
Κανόνες (1), (2), και (3), μπορούν να χρησιμοποιηθούν, στο πλαίσιο μιας διαδικασίας που ονομάζεται "Gaussian εξουδετέρωσης, να μετατρέπουν κάθε τετραγωνικό μήτρα σε μια τριγωνική μήτρα.
Κανόνας (4) μπορούν να χρησιμοποιηθούν για να υπολογιστεί το καθοριστικό μιας τριγωνικής μήτρα.
Ο επίσημος ορισμός των "καθοριστικό:"
Ας M[n](K) χαρακτηρίζει το σύνολο όλων των πινάκων (n * n) πάνω από το πεδίο (K).
Το "καθοριστικό" είναι η μοναδική λειτουργία (F) τέτοια που F:M[n](K) --> K με δύο ιδιότητες:
(1) F είναι εναλλασσόμενο multilinear όσον αφορά τις στήλες (ή σειρές)? Και,
(2) F( I ) = 1.
A "multilinear" λειτουργία (D) μιας (n * n) μήτρα (A) μπορεί να γραφτεί ως: 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]] ) ), όπου το άθροισμα έχει αναλάβει όλα τα (n^n) συνδυασμούς 0 <= k[i] <= (n-1), και όπου e[j] αντιπροσωπεύει σειρά (j) της ταυτότητας μήτρα.
Μια τέτοια λειτουργία μπορεί να χαρακτηριστεί από τις αξίες της D( e[k[0]], e[k[1]], ..., e[k[n-1]] ).
"Εναλλασσόμενο multilinear" λειτουργία είναι μια λειτουργία multilinear, (D), τέτοια που D( ..., e[i], ..., e[i], ... ) = 0, και D( ..., e[i], e[j], ... ) = (-( D( ..., e[j], e[i], ... ) )).
Έτσι, αλλάζουν στήλες (ή γραμμές) αλλάζει το πρόσημο της συνάρτησης.
Επίσης, ο όρος D( e[k[0]], ..., e[k[n-1]] ) είναι μηδέν εάν ο συνδυασμός των k[i] τιμές δεν περιλαμβάνουν όλες τις μοναδικές τιμές? Εάν το σύνολο των αξιών δεν είναι παραδείγματα των αριθμών {0,1,...,(n-1)}.
Εκμίσθωση D( I ) = 1 (D( e[0], e[1], ..., e[n-1] ) = 1), σε συνδυασμό με το εναλλασσόμενο multilinear χαρακτηριστικά που περιγράφονται ανωτέρω, σημαίνει ότι όλες οι τιμές του εναλλασσόμενου multilinear λειτουργία D( e[k[0]], e[k[1]], ..., e[k[n-1]] ) μπορεί να προσδιοριστεί.
Η λειτουργία θα είναι μη μηδενική μόνο εάν το σύνολο των k[i] αξίες είναι παραδείγματα του {0,1,...,(n-1)}, και θα έχει το μέγεθος ενός αν είναι μη μηδενική, και το πρόσημο θα είναι ο αριθμός των swaps που απαιτούνται για να μετατρέψουν τα παραδείγματα για να το ταυτότητα παραδείγματα.
Η αξία των καθοριστικό μιας (n * n) μήτρα (A) μπορεί να υπολογίζεται με την άθροιση των προϊόντων του εντύπου (sgn(p) * A[0,p[0]] * A[1,p[1]] * ... * A[(n-1),p[n-1]]) για κάθε παραδείγματα (p) των αριθμών {0,1,2,...,(n-1)}.
Ο όρος (sgn(p)) δηλώνει την "υπογραφή" (ή swap count) (p) παραδείγματα, όπου (sgn(p)) είναι ίση με (+1) αν (p) είναι ένα "ακόμη παραδείγματα," και είναι ίση με (-1) αν (p) είναι ένα "παράξενο παραδείγματα."
Επειδή υπάρχουν (n!) παραλλαγές των αριθμών {0,1,2,...,(n-1)}, αυτός ο τύπος έχει (n!) summands.
Άλλες διαδικασίες για την υπολογιστική καθοριστικό (παραδείγματα: Gaussian εκείνων που αφορούν την κατάργηση, ή LU factoring, ή επέκτασης από ανηλίκους, κτλ) υπολογίζουν εμμέσως κάποια ιεράρχηση των ενδιάμεσων ποσότητες που επιτρέπουν οι διαδικασίες αυτές να υπολογίζουν το καθοριστικό σε μια τάξη (n^3) βήματα.
Αν ο καθοριστικός παράγοντας της μήτρας είναι μηδέν, η μήτρα δεν έχει αντίστροφο.
Αν ο καθοριστικός παράγοντας της μήτρας είναι μη μηδενική, η μήτρα έχει μια αντεστραμμένη.
Εάν μια μήτρα αντιπροσωπεύει τους συντελεστές ένα γραμμικό σύστημα εξισώσεων και ο καθοριστικός παράγοντας της μήτρας που είναι μηδέν, τότε το σύστημα των εξισώσεων δεν έχει μια μοναδική λύση.
Εάν μια μήτρα αντιπροσωπεύει τους συντελεστές ένα γραμμικό σύστημα εξισώσεων και ο καθοριστικός παράγοντας της μήτρας που είναι μη μηδενικό, τότε το σύστημα των εξισώσεων έχει μια μοναδική λύση.
Ο κώδικας περιλαμβάνει διαδικασίες κάτω από το καθοριστικό για τον υπολογισμό του κάθε τετραγωνικό μήτρα.
Καθοριστικοί παράγοντες για 1*1, 2*2, 3*3, και 4*4 matrices υπολογίζονται από ρητή φόρμουλες.
Χρησιμοποιώντας ρητή φόρμουλες για τον υπολογισμό των καθοριστικών για μικρούς πίνακες είναι πιθανό να υπολογίζουν τα αποτελέσματα ταχύτερα από ό, τι χρησιμοποιώντας μια γενική διαδικασία για τον υπολογισμό αυτών των καθοριστικών παραγόντων.
Η διαδικασία για τη γενική περίπτωση χρήσεις LU factoring, αλλά υπάρχουν πολλές άλλες μέθοδοι της υπολογιστικής καθοριστικούς παράγοντες.
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) διαστάσεων φορέων: πολλαπλή προϊόντος
"σταυρό προϊόντος:" Μια λειτουργία που συνδέεται με μια (d) διαστάσεων χώρο που μετατρέπει τη σειρά του καταλόγου (d-1) (d) διαστάσεων φορέων, { A(0), A(1), ..., A(d-2) }, σε μια (d) διαστάσεων φορέα, έτσι ώστε η λειτουργία έχει την παρακάτω χαρακτηριστικά:
(1) Εάν A(i)=0 (μηδέν φορέα), για κάθε 0 <= i <= (d-2), τότε Cross( A(0), A(1), ..., A(d-2) )=0 (μηδέν φορέα).
Η πολλαπλή προϊόν μιας διατεταγμένης κατάλογος των φορέων είναι μηδέν εάν οποιοδήποτε φορέα στον εν λόγω κατάλογο είναι μηδέν?
(2) Εάν κάθε ζεύγος διανυσμάτων, A(i) και A(j), για κάθε (i!=j) με 0 <= i <= (d-2) και 0 <= j <= (d-2), είναι παράλληλες (Abs(Dot(A(i),A(j))) = Length(A(i)) * Length(A(j))), τότε Cross( A(0), A(1), ..., A(d-2) )=0 (μηδέν φορέα).
Η πολλαπλή προϊόν μιας διατεταγμένης κατάλογος των φορέων είναι μηδέν εάν οποιαδήποτε δύο διανύσματα στον εν λόγω κατάλογο είναι παράλληλες?
(3) Εάν κάθε φορέα A(i) είναι μη μηδενική, και αν A(i) δεν είναι παράλληλη με A(j) για όλους (i!=j) με 0 <= i <= (d-2) και 0 <= j <= (d-2), τότε B = Cross( A(0), A(1), ..., A(d-2) ) είναι τέτοια που Dot(A(i),B)=0 για όλους 0 <= i <= (d-2).
Αν το σταυρό προϊόν μιας διατεταγμένης κατάλογος των φορέων είναι μη μηδενικό, τότε το αποτέλεσμα είναι προϊόν διασχίζουν κάθετα προς κάθε φορέα στον κατάλογο των φορέων που παραγγέλθηκε?
(4) Εάν (c) είναι μια αριθμητική τιμή, τότε 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) ).
Λόγω μιας διατεταγμένης κατάλογο των φορέων, το σταυρό του προϊόντος που παραγγέλθηκε κατάλογος των φορέων με οποιονδήποτε φορέα πολλαπλασιάζεται με μια συγκεκριμένη αριθμητική τιμή ισοδυναμεί με πολλαπλασιασμό του προϊόντος σταυρό του καταλόγου των φορέων που παραγγέλθηκε από την αριθμητική τιμή?
(5) Δεδομένου δύο αριθμούς (i) και (j), όπου (i!=j) και 0 <= i <= (d-2) και 0 <= j <= (d-2), διέταξε και ένα κατάλογο των (d-1) (d) διαστάσεων φορέων, { A(0), A(1), ..., A(d-2) }, διέταξε και άλλο κατάλογο των (d-1) (d) διαστάσεων φορέων, { B(0), B(1), ..., B(d-2) }, τέτοια που B(k) = A(k) για όλους ((k != i) && (k != j)) με 0 <= k <= (d-2), και ότι τέτοιες B(i) = A(j) και B(j) = A(i), τότε Cross( A(0), A(1), ..., A(d-2) ) = (-1) * Cross( B(0), B(1), ..., B(d-2) ).
Αν το σταυρό προϊόν μιας διατεταγμένης κατάλογος των φορέων είναι μη μηδενική, τότε που διαπερνούν το προϊόν έχει το αντίθετο πρόσημο της πολλαπλής προϊόν της ίδιας διέταξε κατάλογο των φορέων με την εξαίρεση των δύο ακριβώς φορείς που ανταλλάσσονται (αντάλλαξαν).
Δεδομένου βάση διανυσμάτων e(k), για 0 <= k <= (d-1), (d) διαστάσεων του χώρου, το σταυρό προϊόν μιας διατεταγμένης (d-1) κατάλογος των φορέων μπορεί να υπολογιστεί με υπολογισμό του προσδιοριστικό της μήτρας (d*d) παρακάτω:
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]
Ο καθοριστικός παράγοντας της μήτρας που δεν θα είναι αρκετές, αλλά θα είναι ένα φορέα, όπου θα υπάρχει αριθμητική συντελεστές στην (d) βάση διανυσμάτων.
Για δισδιάστατο χώρο, και ο φορέας A = ( ax, ay ):
Cross( A )
= Determinant
(
e(x), e(y),
ax, ay
)
= ay * e(x)
- ax * e(y)
= ( ay, -ax )
Το αποτέλεσμα του φορέα να είναι κάθετος προς τον ενιαίο φορέα εισροών.
Εάν η x-y αεροπλάνο θεωρείται ότι x τέτοιες αυξήσεις σε μια rightward κατεύθυνση και y αυξήσεις σε ανοδική κατεύθυνση, τότε το αποτέλεσμα είναι προϊόν που διασχίζουν το ένα τέταρτο-Γυρίστε "δεξιά" σε σχέση με την είσοδο του φορέα.
Παραδείγματα προϊόντων σταυρό στο δισδιάστατο χώρο:
Cross( e(x) ) = (-1) * e(y)
Cross( e(y) ) = e(x)
Για τρισδιάστατο χώρο, και των φορέων του ιού A = ( ax, ay, az ) και 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) )
Παραδείγματα προϊόντων σταυρό στο τρισδιάστατο χώρο:
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)
Για τέσσερις διαστάσεις του διαστήματος, και των φορέων του ιού A = ( ax, ay, az, aw ), B = ( bx, by, bz, bw ), και 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) )
Παραδείγματα σταυρό προϊόντα σε τέσσερις διαστάσεις χώρου:
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)
Ο κώδικας παρακάτω υπολογίζει την διασταύρωση του προϊόντος από δύο ή περισσότερους φορείς.
Η δοκιμή παραδείγματα δείχνουν ότι η μη μηδενικό αποτέλεσμα διανύσματα είναι κάθετα προς τη συμβολή όλων των φορέων.
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
"matrix inverse:" Μια πλατεία μήτρας σχετίζεται με μια άλλη πλατεία της μήτρας το ίδιο μέγεθος τέτοιο ώστε το προϊόν των δύο πινάκων είναι η "ταυτότητα μήτρα."
Εάν μια μήτρα έχει μια συνδεδεμένη αντίστροφη μήτρα, υπάρχει μόνο μία τέτοια αντίστροφη μήτρα.
Αν ο καθοριστικός παράγοντας της μήτρας είναι μηδέν, τότε ο πίνακας ονομάζεται "ενικό," και η μήτρα δεν έχει συνδεδεμένες αντίστροφη μήτρα.
Ο κώδικας παρακάτω υπολογίζει το αντίστροφο της κάθε πλατεία, μη ενικό μήτρα.
Inverses για 1*1, 2*2, 3*3, και 4*4 matrices υπολογίζονται από ρητή φόρμουλες.
Χρησιμοποιώντας ρητή φόρμουλες μπορεί να είναι ταχύτερη από τη χρήση μιας γενικής διαδικασίας.
Η ρητή φόρμουλες είναι επίσης αξιόπιστος.
Η διαδικασία για τη γενική περίπτωση χρήσεις LU factoring, αλλά υπάρχουν πολλές άλλες μέθοδοι computing inverses των πινάκων.
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 Multiplicative προϊόν της μήτρας και ένα διανυσματικό
Η multiplicative προϊόν μιας μήτρας (M) και (a) φορέας είναι φορέας (b)? (M)*(a) = (b).
Matrix (M) "μεταμορφώνει" φορέα (a) να φορέα (b).
Οι παρακάτω κώδικα multiplicative υπολογίζει το προϊόν της μήτρας και ενός φορέα.
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 Χρησιμοποιώντας ομοιογενή matrices να μεταφράσει ομογενή διανύσματα
"μετάφραση:" Μια πράξη T(s), που καθορίζεται από ένα συγκεκριμένο φορέα (s), που μετατρέπει κάθε φορέα (r) σε αντίστοιχο φορέα (r + s)? T(s) * (r) = (r + s).
T(-s) είναι το αντίστροφο της T(s)? Inverse(T(s)) = T(-s)? T(-s) * (r) = (r - s).
Μια μετάφραση λειτουργία μπορεί να εκπροσωπείται από μια μήτρα, αυτές που τον πολλαπλασιασμό της μετάφρασης μήτρα από ένα φορέα που έχει ως αποτέλεσμα να γίνει το φορέα.
Η συμβολή της μετάφρασης στη λειτουργία των κατασκευαστικών στοιχείων του φορέα αποτέλεσμα δεν εξαρτώνται άμεσα ή έμμεσα, σχετικά με τα στοιχεία του αρχικού φορέα.
Έτσι, μια μετάφραση μήτρα πρέπει να είναι διαρθρωμένη κατά τρόπο που να της επιτρέπει να συμβάλλει μια μετάφραση για το αποτέλεσμα χωρίς να επηρεαστεί, άμεσα ή έμμεσα, από τα στοιχεία του αρχικού φορέα.
Ένα "ομοιογενές φορέας" είναι φορέας με μια τελική συνιστώσα ίση με 1.
Ένα "ομοιογενές μήτρα" είναι ένας πίνακας με την τελική σειρά με όλες τις ενδείξεις ίση με 0 εκτός από την τελική θέση της 1.
Η multiplicative ένα ομοιογενές προϊόν της μήτρας και άλλο ομογενή μήτρα είναι ένα ομοιογενές μήτρα.
Η multiplicative ένα ομοιογενές προϊόν της μήτρας, και η ομοιογένεια του φορέα είναι ένα ομοιογενές φορέα.
Ένα ενδιαφέρον χαρακτηριστικό αυτού του προϊόντος είναι ότι οι εγγραφές στην τελευταία στήλη του ομογενή μήτρα, με εξαίρεση την τελική θέση, είναι να προστεθεί άμεσα τα στοιχεία για το αποτέλεσμα του φορέα και δεν θα επηρεαστεί, άμεσα ή έμμεσα, από τα στοιχεία των το αρχικό φορέα.
Έτσι, μια μετάφραση λειτουργία μπορεί να εκπροσωπείται από έναν ομογενή μήτρα, και μια τέτοια μήτρα μπορεί να χρησιμοποιηθεί για να μεταφράσει ομογενή διανύσματα.
Αν θέλουμε να εκπροσωπήσει τις θέσεις και τις κατευθύνσεις (d) διαστάσεων στο χώρο και θέλουμε να χρησιμοποιήσετε πίνακες για να μεταφράσει τις θέσεις, τότε οι πίνακες και τα ομοιογενή ομογενή διανύσματα μπορούν να χρησιμοποιηθούν.
Ωστόσο, οι φορείς πρέπει να έχουν ομοιογενή (d+1) συστατικά, και το ομοιογενές μητρών πρέπει να έχουν ((d+1)*(d+1)) εγγραφές.
Το τελικό συστατικό ενός ομοιογενούς φορέα και η τελική σειρά του ένα ομοιογενές μήτρα δεν αντιστοιχούν σε καμία από τις συντεταγμένες (d) που θέλουμε να εκπροσωπούμε στην (d) διαστάσεων χώρο, αλλά αντί να χρησιμεύσει μόνο για να καταστήσει δυνατή την matrices να κάνει κάποιες ενέργειες ανεξάρτητα από το (d) συντονίσουν τις αξίες.
Χρησιμοποιώντας ομοιογενή μήτρες και ομοιογενή φορέων καθιστά ευκολότερο για τις σύνθετες μετατροπές και μετασχηματισμούς εφαρμόζεται με φορείς, αλλά και άλλες εργασίες γίνονται πιο άβολο.
Εκπροσωπώντας τις θέσεις ή τις κατευθύνσεις στον χώρο (d) διαστάσεων χρησιμοποιώντας ομογενή διανύσματα σημαίνει ότι οι φορείς θα έχουν (d+1) συστατικά, με το τελικό στοιχείο ίσο με 1.
Computing την τελεία ομοιογενές προϊόν των δύο φορέων με τη χρήση τακτικών dot-προϊόν λειτουργία θα πρέπει να συνδυάζει την dot σιωπηρά προϊόν της πρώτης (d) συστατικά με μια άσχετη αξία των 1 οφείλεται στο τελικό συνιστώσες της 1.
Ως εκ τούτου, για να πάρετε το dot προϊόν της σχετικής (d) συνιστώσες του ομογενή φορέα (με (d+1) συστατικά), το dot ομοιογενές προϊόν φορείς θα πρέπει να γίνει χρησιμοποιώντας έναν ειδικό "φορέα dot ομοιογενές προϊόν" λειτουργεί απλώς ότι παραλείπει το τελικό συστατικό των φορέων.
Εναλλακτικά, οι dot προϊόν θα μπορούσε να υπολογιστεί με την τακτική λειτουργία dot προϊόντος με την προϋπόθεση ότι το ποσό των 1 θα πρέπει να αφαιρείται από το αποτέλεσμα πριν από την ερμηνεία της ως αποτέλεσμα που αφορούν την (d) σχετικές συντεταγμένες.
Ο κώδικας που ακολουθεί δείχνει πώς μια μήτρα μπορεί να σχηματιστεί ότι μπορούμε στη συνέχεια να χρησιμοποιηθεί για να μεταφράσει ένα διανυσματικό.
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) διαστάσεων χώρο: εναλλαγών
"εναλλαγή στην (d) διαστάσεων χώρο:" Μια πράξη (R) καθορίζονται από ένα δισδιάστατο εναλλαγή (S) αεροπλάνο, μια γωνία περιστροφής (t), και ένα κεντρικό σημείο περιστροφής (c), που μετατρέπει κάθε σημείο (p) σε αντίστοιχο σημείο (q), τέτοια ώστε:
(1) Length( q - c ) = Length( p - c )? Η απόσταση μεταξύ του σημείου και το νέο κέντρο περιστροφής το σημείο είναι το ίδιο με την απόσταση μεταξύ του σημείου και την αρχική εναλλαγή κεντρικό σημείο.
Έτσι, το νέο σημείο στην επιφάνεια της συγκεκριμένης (d) την τρισδιάστατη σφαίρα?
(2) Let (P) είναι ένα επίπεδο που είναι παράλληλο προς την εναλλαγή αεροπλάνο (S) και περιέχει σημείο (p).
Σημείο (q) θα είναι στο αεροπλάνο (P).
Έτσι, το νέο σημείο είναι για μια συγκεκριμένη δισδιάστατο επίπεδο του (d) διαστάσεων χώρο?
(3) Dot( (q - c), (p - c) ) = Length( q - c ) * Length( p - c ) * Cos( t )? Το dot προϊόν του φορέα από το κέντρο περιστροφής το σημείο με το αρχικό σημείο και το φορέα από το κέντρο περιστροφής το σημείο με το νέο σημείο είναι ίση με το γινόμενο των μηκών των δύο αυτών φορέων και της cosine της εναλλαγής γωνία.
Έτσι, το νέο σημείο στην επιφάνεια της συγκεκριμένης (d) διαστάσεων κώνου?
Καταναγκασμό (1) όρια στο νέο σημείο στην επιφάνεια της συγκεκριμένης (d) την τρισδιάστατη σφαίρα.
Προϋπόθεση (2) περιορίζει επιπλέον το νέο σημείο σε μια συγκεκριμένη δισδιάστατο επίπεδο του (d) διαστάσεων χώρο.
Ως εκ τούτου, το νέο σημείο πρέπει να είναι για μια συγκεκριμένη δισδιάστατο κύκλο (d) διαστάσεων χώρο.
Προϋπόθεση (3) περιορίζει επιπλέον το νέο σημείο σε μια συγκεκριμένη (d) διαστάσεων κώνου.
Ως εκ τούτου, το νέο σημείο πρέπει να βρίσκεται σε ένα συγκεκριμένο σημείο (για Cos( t ) = 1 ή Cos( t ) = (-1)) ή σε ένα από δύο πιθανά σημεία (για (-1) < Cos( t ) < 1).
Ένα τελευταίο εμπόδιο καλείται να επιλέξει μία από τις δύο πιθανά σημεία που προκύπτουν από τους περιορισμούς που αναφέρθηκαν παραπάνω.
Ας T(s) αντιπροσωπεύει μια "μεταφραστική" λειτουργία, όπου είναι μια αυθαίρετη (s) φορέα, τέτοια που T(s) * (r) = (r + s), και Inverse(T(s)) * (r) = (r - s).
Μια εναλλαγή R(S,t,c), για αεροπλάνο S, γωνία t, και κεντρικό σημείο c, μπορούν να εκφράζονται σε μετάφραση επιχειρήσεις και μια εναλλαγή με ένα κεντρικό σημείο σε προέλευσης: R(S,t,c) = T(c) * R(S,t,0) * T(-c) = T(c) * R(S,t,0) * Inverse(T(c)).
Ας R(S,t) αντιπροσωπεύει μια εναλλαγή λειτουργίας με εναλλαγή σε κεντρικό σημείο της προέλευσης των (d) διαστάσεων χώρο.
Έτσι, κάθε αυθαίρετη περιστροφής μπορεί να εκφραστεί ως: R(S,t,c) = T(c) * R(S,t) * T(-c) = T(c) * R(S,t) * Inverse(T(c)).
Ας εξετάσουμε δύο διαστάσεων εναλλαγή (S) αεροπλάνα που περιλαμβάνουν την προέλευση των (d) διαστάσεων χώρο και είναι παράλληλα σε δύο άξονες που συντονίζουν.
Ας (a) και (b) να είναι ακέραιοι οι οποίοι αντιπροσωπεύουν το συντονισμό άξονες στις (d) διαστάσεων χώρο, τέτοιες που 0 <= a <= (d-1), και 0 <= b <= (d-1), και (a != b).
Ορίζουμε R(a,b,t) να είναι ένα εκ περιτροπής για την προέλευση των (d) διαστάσεων χώρο, με περιστροφή επίπεδο παράλληλο να συντονίσουν άξονες που υποδεικνύονται από το συντονισμό των δεικτών (a) και (b), και με σχηματίζει γωνία (t), R(a,b,(pi/2)) τέτοια που θα μετατρέψει μια θετική αξία για το συντονισμό συντονίσουν άξονα που υποδεικνύονται από (a) να συντονίσει μια θετική αξία για το συντονισμό του άξονα που υποδεικνύονται από (b)? παράδειγμα: R(a,b,(pi/2)) * e(a) = e(b), όπου e(k) δείχνει μια μονάδα βάσης αντίστοιχο φορέα για το συντονισμό άξονα (k).
Ο ορισμός αυτός προβλέπει το τελικό disambiguation αναγκαίο να προσδιοριστεί πλήρως το νέο στοιχείο που παράγεται από ένα εκ περιτροπής.
Μια εναλλαγή σημεία που μετακινείται κατά μήκος του άξονα a προς τον άξονα b σύμφωνα με σχηματίζει γωνία t είναι ισοδύναμη με την εναλλαγή των σημείων που μετακινείται κατά μήκος του άξονα b προς τον άξονα a με σχηματίζει γωνία (-t)? Δηλαδή, R(a,b,t) = R(b,a,(-t)).
Αν (d >= 2), τότε (d) διαστάσεων χώρος έχει ((d*(d-1))/2) ζεύγη συντονίζουν άξονες.
Έτσι, για (d >= 2), (d) διαστάσεων χώρος έχει ((d*(d-1))/2) διακριτή άξονα περιστροφής στοίχιση αεροπλάνα.
(Παραδείγματα: d=2: (x-y αεροπλάνο)? d=3: (x-y αεροπλάνο, y-z αεροπλάνο, αεροπλάνο z-x)? d=4: (x-y αεροπλάνο, y-z αεροπλάνο, z-x αεροπλάνο, x-w αεροπλάνο, z-w αεροπλάνο, αεροπλάνο w-y).)
Ο κώδικας που ακολουθεί δείχνει πώς μια μήτρα μπορεί να χρησιμοποιηθεί για να εκπροσωπήσει την εναλλαγή R(a,b,t).
Ένα παράδειγμα είναι δείξει ότι χρησιμοποιεί μια εναλλαγή μήτρα σε μια ομοιογενή τρόπο για να περιστρέψετε ένα ομοιογενές φορέα σε φορέα νέων ομογενών.
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