Die Programmieraufgabe ist in mehrere kleine Teile zerlegt. Bevor es losgeht alles von Blatt 6 (diesen Link klicken) herunterladen und in den Numerik Ordner packen. Das Makefile hat sich nicht verändert sondern ist nur zur Sicherheit dabei...
In jeder Aufgabe ist erklärt:
Und los geht's:
Das Hauptprogramm ist bereits fix und fertig:
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
DEVector alpha(3);
GEMatrix A(4,3);
A = 2, 1, 3,
4, 4, 9,
6, 9, 16,
8, 16, 24;
VectorView v = A(_,1);
alpha(1) = norm2(v);
cout << alpha << endl;
}
Was soll dieses Programm tun?
VectorView v = A(_,1);
Damit dieses Programm funktioniert muss aber noch die Funktion norm2 in der Datei blatt6.h implementiert werden:
template <typename VX>
double
norm2(const DenseVector<VX> &x)
{
// Hier Programm-Code einfuegen.
// statt 0 soll die euklidische Norm von x
// zurueckgegeben werden:
return 0;
}
Das sollte nicht weiter schwer sein, denn bekanntlich gilt
. Im
schlimmsten Fall programmiert man das mit einer for-Schleife. Allerdings kann man
das Skalarprodukt in FLENS auch einfach mit x*x erhalten. Wenn man daraus noch die
Wurzel zieht, dann ist man schon fertig. Eine einzige Programmzeile sollte also reichen.
Dabei ist folgendes zu beachten:
double alpha = x*y;

Auch folgendes Programm ist bereits fertig. In blatt6.h muss dafür nur noch die Funktion householder programmiert werden.
Ziel der Aufgabe ist, dass

berechnet wird. Der Vektor v muss also nicht normiert sein. Das Ergebnis der Aufgabe sollte aus den Übungen bekannt sein.
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
DEVector x(3);
x = 2, 1, 2;
DEVector v(3);
v = 2, -2, 2;
householder(v, x);
cout << "Hx = " << x << endl;
}
Damit alles funktioniert muss in blatt6.h die Funktion householder programmiert werden:
template <typename VV, typename VX>
void
householder(const DenseVector<VV> &v, DenseVector<VX> &x)
{
// Hier soll x mit H*x ueberschrieben werden,
}
Dabei muss man (noch darf man) weder eine Einheitsmatrix aufstellen noch weitere Vektoren anlegen. Auch dies kann mit einer einzigen Zeile programmiert werden!
Aufgabe 3 ist eigentlich gar keine Aufgabe sondern nur ein Test! Sogar ein doppelter Test
Erklärt was diese Programm tut und erklärt die Ausgabe des Programm:
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
GEMatrix A(4,3);
A = 2, 1, 3,
4, 4, 9,
6, 9, 16,
8, 16, 24;
DEVector v(4);
v = A(_,1);
v(1) = v(1) + norm2(v);
int n = A.numCols();
for (int k=1; k<=n; ++k) {
VectorView x = A(_,k);
householder(v, x);
}
cout << "A = " << A << endl;
}
Hinweis: In diesem Programm steht
DEVector v(4); v = A(_,1);
Hier ist also v keine View. Deshalb enthät v eine Kopie der ersten Spalte. Und deshalb wird A auch nicht verändert, wenn v verändert wird.
In dieser Aufgabe muss nur das Programm aufgabe4.cc veraendert werden.
Sei
dann wählt man im ersten Schritt der QR-Zerlegung den
Vektor
für die Householder Transformation so:
setze Vektor 
.
den ersten Einheitsvektor.
Das soll nun als Programm realisiert werden:
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
DEVector alpha(3);
GEMatrix A(4,3);
A = 2, 1, 3,
4, 4, 9,
6, 9, 16,
8, 16, 24;
VectorView v = A(_,1);
// Hier v veraendern. Und zwar so:
// Sei alpha die euklidische Norm der ersten Spalte von A
// falls A(1,1) > 0, dann
// setze v := v + alpha*e1
// sonst
// setze v := v - alpha*e1
int n = A.numCols();
for (int k=2; k<=n; ++k) {
VectorView x = A(_,k);
householder(v, x);
}
cout << "alpha = " << alpha << endl;
cout << "A = " << A << endl;
}
Was in Aufgabe 4 noch im Hauptprogramm getan wurde soll nun in die Funktion qrFactorStep (die wie der Name vage andeute einen Schritt der QR-Faktorisierung durchführt) gepackt werden:
template <typename MA>
double
qrFactorStep(GeMatrix<MA> &A)
{
typedef typename GeMatrix<MA>::VectorView VectorView;
int n = A.numCols();
VectorView v = A(_,1);
// Hier Programm-Code einfuegen.
// Satt 0 soll die Funktion alpha zurueckgeben:
return 0;
}
Damit sollte das Programm die gleiche Ausgabe liefern wie in Aufgabe 4:
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
DEVector alpha(3);
GEMatrix A(4,3);
A = 2, 1, 3,
4, 4, 9,
6, 9, 16,
8, 16, 24;
alpha(1) = qrFactorStep(A);
cout << "A = " << A << endl;
cout << "alpha = " << alpha << endl;
}
Noch ein paar Programm Zeilen und die QR-Zerlegung ist fertig implementiert.
In blatt6.h muss dafür die Funktion qr fertig programmiert werden:
template <typename MA, typename VX>
void
qr(GeMatrix<MA> &A, DenseVector<VX> &alpha)
{
typedef typename GeMatrix<MA>::View MatrixView;
int m = A.numRows();
int n = A.numCols();
// Hier Programm-Code einfuegen
}
Dazu sollten circa 4 Zeilen Programm-Code ausreichend sein! Die Funktion soll A mit der QR-Zerlegung überschreiben und die Diagonale von R in alpha speichern.
Wie man das so leicht hinbekommt? Ganz einfach! Die Funktion qr ruft selber die Funktion qrFactorStep auf. Dabei übergibt sie verschieden Teilmatrizen von A:
Einen Matrixausschnitt kann man so festlegen:
MatrixView B = A(_(2,m),_(2,n));
Dann bezieht sich B auf alle Elemente von A, bei denen
#include <blatt6.h>
typedef GeMatrix<FullStorage<double, ColMajor> > GEMatrix;
typedef DenseVector<Array<double> > DEVector;
typedef GEMatrix::VectorView VectorView;
int
main()
{
DEVector alpha(3);
GEMatrix A(4,3);
A = 2, 1, 3,
4, 4, 9,
6, 9, 16,
8, 16, 24;
qr(A, alpha);
cout << "A = " << A << endl;
cout << "alpha = " << alpha << endl;
}