ハウスホルダー変換を利用した直交化

最初に

 ハウスホルダ変換を利用した直交化手法は,Gram-Schmidtの直交化よりも低速であるが, 1回の変換の試行における誤差は少ない.また,3重対角化への応用が容易であり, 行列の固有値を求めるための前処理として有用な技法である.

定義式

 ここでは,m × m の大きさの行列 A のQR分解について述べる.
 A = (a1, a2, ..., am) において, A(1) = Q1 A = [a1(1), a2(1), ..., am(1)] とおいて, ベクトル a1(1) = Q1 a1 が, 最初の要素以外がゼロとなるように Q1 を定める.

 ここで, Inn × n の大きさの単位行列であり,記号 ' は転置行列を表す. Q1 は鏡像変換である. 上式より, a1 - a1(1) = 2 t1 k1 ( k1 = t1' a1 ) である. Q1 は直行行列であるから,||a1|| = ||a1(1)||.従って, (a1(1))' = (a11(1), 0, 0, ..., 0) (a11(1) ≧ 0) とおくと, a11(1) = ||a1||.更に, ||a1 - a1(1)||2 = 4 k12 ||t1||2 = 4 k12 より,

となる.従って,

とおけばよい.
 一般に, tj (j ≧ 2) を求めるには, (n, p) 型行列 A(j-1)j 番目の 列ベクトル aj(j - 1) の上から (j - 1) 個の成分を取り除いた (n - j + 1) 次元ベクトルを

とおくと,t1 と同様,

となる.
 このようにして得られる t1, t2, ..., tm を用いて,

および,

を構成すると,変換 Q = (Q1 Q2 ・・・ Qm) により, Q'A が上三角行列 R となる.従って,左辺から Q を乗じることによって, A = QR となり, QR分解が得られる.

参考文献
 柳井晴夫,竹内啓, "射影行列・一般化逆行列・特異値分解", 東京大学出版会, (1983).

プログラムリスト

上記の文字に対応していない場合があります.コメントを参照してください.


#define C 3			// column
#define R 3			// rank
double factors[C * R]; //行列の定義			//この行はで宣言
void MultipleAbove(CMatrix* a, int seigen = 0);	//この行はで宣言
void MultipleBehind(CMatrix* a, int seigen = 0);	//この行はで宣言
BOOL HouseHolder(double *NORM, double *det);	//この行はで宣言

//ハウスホルダー変換を利用した直交化
BOOL CMatrix::HouseHolder(double *NORM, double *det)
{
	register double len, sigma;
	register int i, j, k, l, cr;
	cr = C * R;
	CMatrix* Q= new CMatrix(R, R);
	CMatrix* A= new CMatrix(C, R, factors);
	double* t= new double[R];

	for(k = 0; k < R; k++){

		//(a, 0, 0, ・・・, 0)化するベクトル t を計算
		for(l = R-k, len = 0.0, i=0; i < l; i++)
			t[i] = A->factors[(k + i) * R + k], len += t[i] * t[i];

		//lenとt[0]は同符号にして桁落ちを回避する
		len = (t[0] < 0)? -sqrt(len): sqrt(len);
		sigma = len, t[0] += len,
		len *= t[0];			//len = sqrt(len * (len + A->factors[k * R + k]) * 2.0);
						//sqrtを使わないであとで手直しをする

		if(len == 0.0){
			if(k == R - 1) break;	//最後の行ではlen==0は操作の必要なしを表す
			delete Q;
			delete A;
			delete [] t;
			return FALSE;
		}

		//Q'を計算
		//Q->unitmatrix(R);
		for(i = 0; i < R; i++){
			for(j = 0; j < R; j++){
				Q->factors[i * R + j] = 0.0;
			}
			Q->factors[i * (R + 1)] = 1.0;
		}

		for(i = 0; i < l; i++){
			for(j = 0; j < l; j++){
				Q->factors[(k + i) * R + k + j] -= t[i] * t[j] /len; //lenで割ってt[i],t[j]を単位行列化する
			}
		}

		//ΠQ×Aの計算(理論的に  [a11  z~])
		//ΠQ×Aの計算(理論的に  [ y   B ])

		//AにQを前から掛ける
		A->MultipleAbove(Q);

		//一応零に近くなるが,0.0を代入して保証する
		for(i = k + 1; i < C; i++){
			A->factors[i * R + k] = 0.0;
		}

		//ΠQの計算
		if(k>0){
			//後ろからQをかける
			MultipleBehind(Q);
		}else{
			for(i = 0;i < cr; i++){
				factors[i] = Q->factors[i];
			}
		}
	}

	//直交行列のノルムの計算
	if(NORM != NULL){
		for(k = 0; k < R; k++){
			NORM[k] = A->factors[k * (R + 1)] * A->factors[k * (R + 1)];
		}
	}

	//元の行列のデターミナントの計算
	if(det ! =NULL){
		for(*det = 1.0, k = 0; k < R; k++){
			*det *= A->factors[k * (R + 1)];
		}
	}

	delete Q;
	delete A;
	delete [] t;

	return TRUE;
}

//(*this) = (a) * (*this)を計算する //seigen:行数制限(いらないデータは計算しない)
void CMatrix::MultipleAbove(CMatrix* a, int seigen) //前からaを掛ける
{
	if(a->R != C){
		AfxMessageBox("CMatrix operator multiple error");
		exit(0);
	}

	int i, j, k;
	double sam;
	double* stack;
	stack = new double [C * R]; //行と列を入れ替えたスタックを用意する

	for(i = 0; i < C; i++){
		for(j = 0; j < R; j++){
			stack[j * C + i]=factors[i * R + j];
		}
	}

	if(C != a-> C){
		//新しい大きさのマトリクスに変える
		delete [] factors;
		C = a->C;
		factors = new double[C * R];
	}

	if(seigen == 0) seigen = C;			//seigen==0は制限なしの意味

	for(i = 0; i < seigen; i++){
		for(j = 0; j < R; j++){
			for(sam = 0.0, k = 0; k < C; k++){
				sam += a->factors[i * a-> R + k]*stack[j * C + k];
			}
			factors[i * R + j] = sam;
		}
	}
	delete [] stack;
}

//(*this)=(*this)*(a)を計算する //seigen:行数制限(いらないデータは計算しない)
void CMatrix::MultipleBehind(CMatrix* a, int seigen) //後ろからaを掛ける
{
	if(R != a->C){
		AfxMessageBox("CMatrix operator multiple error");
		exit(0);
	}

	int i, j, k;
	double sam;
	double* stack;
	stack = new double [C * R];

	for(i = 0;i < C * R; i++){
		stack[i] = factors[i];
	}

	if(R != a->R){
		//新しい大きさのマトリクスに変える
		delete [] factors;
		R = a->R;
		factors = new double[C * R];
	}

	if(seigen == 0) seigen = C;			//seigen==0は制限なしの意味

	for(i = 0; i < seigen; i++){
		for(j = 0;j < R; j++){
			for(sam = 0.0, k = 0; k < R; k++){
				sam += stack[i * R + k] * a-> factors[k * a->R + j];
			}
			factors[i * R + j] = sam;
		}
	}
	delete [] stack;
}

言語は一応C++です.(C言語も混ざっていますが).
バグがありましたら連絡してください.お願いします.

戻る
2002/4
by Hiroyuki Koga