跳过正文
  1. Docs/

矩阵运算

·4964 字·10 分钟· loading · loading · · ·
Algorithms Mathematical Math C/C++
Pacyu
作者
Pacyu
程序员
目录

这篇主要介绍一些矩阵运算相关算法。

矩阵乘法
#

矩阵相乘:\( C = AB \)

普通算法
#

  • 时间复杂度 \( O(N^3) \)
for (int i = 0; i < N; i++)
    for (int j = 0; j < N; j++)
        for (int k = 0; k < N; k++)
            c[i][j] += a[i][k] * b[k][j];

Strassen算法
#

  • 时间复杂度 \( O(N^{lg7}) \)
void Strassen_algo(vector<vector<float>> & c, vector<vector<float>>  a, vector<vector<float>>  b)
{
}

线性方程组
#

\( n \) 个未知数 \( m \) 个方程的线性方程组

$$ \left( \begin{matrix} a_{00}x_0 & a_{01}x_1 & \cdots & a_{0n}x_n &= b_0 \\ a_{10}x_0 & a_{11}x_1 & \cdots & a_{1n}x_n &= b_1 \\ \vdots \\ a_{m0}x_0 & a_{m1}x_1 & \cdots & a_{mn}x_n &= b_m \end{matrix} \right. $$

将上式写为以向量 \( x \) 为未知元的向量方程

$$ Ax = b \quad (1) $$

方程是否有解
#

设 \( B = (A, b) \) 为增广矩阵,\( R(A) \) 是矩阵 \( A \) 的秩,\( R(A, b) \) 是增广矩阵 \( B \) 的秩。

  • 无解的充分必要条件是 \( R(A) < R(A, b) \);
  • \( n \) 元线性方程组 \( Ax = b \quad \) 有唯一解的充分必要条件是 \( R(A) = R(A, b) = n \);
  • 有无限多解的充分必要条件是 \( R(A) = R(A, b) \leq n \).

LUP分解原理
#

\( LUP \) 分解的基本思想就是找出三个 \( n \times n \) 矩阵 \( L, U, P \),满足

$$ PA = LU \quad (2) $$

其中,\( L \) 是一个单位下三角矩阵,\( U \) 是一个上三角矩阵,\( P \) 是一个置换矩阵。我们称满足式 \( (2) \) 的矩阵 \( L, U, P \) 为矩阵 \( A \) 的 \( LUP \) 分解

可以证明,如果矩阵 \( A \) 是一个非奇异矩阵,那么我们一定能找到 \( L, U, P \) 分解满足上式。

这有什么用呢?

我们先对 \( (1) \) 进行置换,在等式 \( Ax = b \) 两边同时乘以 \( P \) 置换,得到:

$$ PAx = Pb $$

利用 \( (2) \) 式有 \( LUx = Pb \)。

设 \( y = Ux \),其中 \( x \) 就是要求解的向量解。首先,通过一种称为“正向替换”的方法求解单位下三角系统

$$ Ly = Pb \quad (3) $$

得到未知向量 \( y \)。然后,通过一种称为“反向替换”的方法求解上三角系统

$$ Ux = y \quad (4) $$

得到向量解 \( x \)。由于置换矩阵 \( P \) 是可逆的,在等式 \( (2) \) 两边同时乘以 \( P^{-1} \),于是 \( A = P^{-1}LU \),因此,向量 \( x \) 就是 \( Ax = b \) 的解

$$ \begin{aligned} Ax &= P^{-1}LUx\\ &= P^{-1}Ly\\ &= P^{-1}Pb\\ &= b \end{aligned} $$

正向替换与反向替换
#

进一步看看正向替换与反向替换是如何进行的。

  • 正向替换

已知 \( L, P \) 和 \( b \),正向替换可在 \( O(n^2) \) 的时间内求解单位下三角系统 \( (3) \)。

$$ Pb = \begin{bmatrix} p_{0,0} & p_{0,1} & \cdots & p_{0,n} \\ p_{1,0} & p_{1,1} & \cdots & p_{1,n} \\ \vdots & \vdots & \cdots & \vdots \\ p_{n,0} & p_{n,1} & \cdots & p_{n,n} \end{bmatrix} \begin{bmatrix} b_0 \\ b_1 \\ \vdots \\ b_n \end{bmatrix} = \begin{bmatrix} \sum_{j = 0}^{n}p_{0,j} \cdot b_j \\ \sum_{j = 0}^{n}p_{1,j} \cdot b_j \\ \vdots \\ \sum_{j = 0}^{n}p_{n,j} \cdot b_j \end{bmatrix} $$

为了方便起见,这里用一个数组 \( \pi[0…n] \) 简洁地表示置换 \( P \)。对 \( i = 0, 1, 2, \cdots, n \),元素 \( \pi[i] \) 表示 \( P_{i, \pi[i]} = 1 \),并且对 \( j \neq \pi[i] \) 有 \( P_{ij} = 0 \)。因此,\( PA \) 第 \( i \) 行第 \( j \) 列的元素为 \( a_{\pi[i],j} \),\( Pb \) 的第 \( i \) 个元素为 \( b_{\pi[i]} \)。因为 \( L \) 是单位下三角矩阵,我们可以重写等式 \( (3) \) 为:

$$ \begin{bmatrix} y_0 & & & & & & & & &= b_{\pi[0]} \\ l_{10}y_0 & + & y_1 & & & & & & &= b_{\pi[1]} \\ l_{20}y_0 & + & l_{21}y_1 & + & y_2 & & & & &= b_{\pi[2]} \\ \vdots \\ l_{n0}y_0 & + & l_{n1}y_1 & + & l_{n2}y_2 & + & \cdots & + & y_n &= b_{\pi[n]} \end{bmatrix} $$

第一个等式可以求出 \( y_0 \),我们把它代入第二个等式,求出

$$ y_1 = b_{\pi[1]} - l_{10}y_0 $$

在将 \( y_1, y_2 \) 代入第三个等式,得到

$$ y_2 = b_{\pi[2]} - (l_{20}y_0 + l{21}y_1) $$

一般的,我们把 \( y_0, y_1, \cdots, y_{i - 1} \) “正向替换”到第 \( i \) 个等式中,就可以求解 \( y_i \):

$$ y_i = b_{\pi[i]} - \sum_{j = 0}^{i - 1} l_{ij}y_{j} $$

  • 反向替换

与正向替换类似,求解上三角系统等式 (4)。

$$ \begin{bmatrix} u_{0,0}x_0 & + & u_{0,1}x_1 & + & \cdots & + & u_{0,n-2}x_{n-2} & + & u_{0,n-1}x_{n-1} & + & u_{0,n}x_n &= y_{0} \\ & & u_{1,1}x_1 & + & \cdots & + & u_{1,n-2}x_{n-2} & + & u_{1,n-1}x_{n-1} & + & u_{1,n}x_n &= y_{1} \\ & & & & & & & & & & \vdots \\ & & & & & & u_{n-2,n-2}x_{n-2} & + & u_{n-2,n-1}x_{n-1} & + & u_{n-2,n}x_n &= y_{n - 2} \\ & & & & & & & & u_{n-1,n-1}x_{n-1} & + & u_{n-1,n}x_n &= y_{n - 1} \\ & & & & & & & & & & u_{n,n}x_n &= y_{n} \end{bmatrix} $$

这里我们先求解第 \( n \) 个等式,然后往前一个等式代,因此可以如下相继求出 \( x_n, x_{n - 1}, \cdots, x_1 \) 的解:

$$ \begin{aligned} x_n &= \frac{y_{n}}{u_{n,n}} \\ x_{n - 1} &= \frac{y_{n - 1} - u_{n - 1, n}x_n}{u_{n - 1,n - 1}} \\ \vdots \end{aligned} $$

一般的,有

$$ x_i = \frac{y_i - \sum_{j = i + 1}^{n} u_{ij}x_{j}}{u_{ij}} $$

计算LUP分解
#

我们该如何找到这样的 \( L, U, P \) 矩阵呢?

通常在 \( LUP \) 分解中包含一个置换矩阵 \( P \) 的原因是为了避免矩阵 \( A \) 中的主元 \( a_{i,i} = 0 \),即矩阵对角线上的数。

但如果矩阵 \( A \) 的对角线上的数都不为 \( 0 \),我们有一个 \( LU \) 分解算法就可以计算出矩阵 \( L, U \)。

下面先给出 \( LU \) 分解算法的实现,之后再分析算法原理。

LU分解算法实现
#

示例:

$$ \begin{bmatrix} 2 & 3 & 1 & 5 \\ 6 & 13 & 5 & 19 \\ 2 & 19 & 10 & 23 \\ 4 & 10 & 11 & 31 \\ \end{bmatrix} $$

#include <iostream>
#include <vector>
const int N = 4;
using namespace std;
void lu(vector<vector<float>> &l, vector<vector<float>> &u, float a[][N])
{
    for (int i = 0; i < N; i++)
	{
		l[i][i] = 1;
		for (int j = i + 1; j < N; j++)
			l[i][j] = 0;
	}
	for (int i = 1; i < N; i++)
		for (int j = 0; j < i; j++)
			u[i][j] = 0;
	for (size_t i = 0; i < N; i++)
	{
		u[i][i] = a[i][i];
		for (size_t j = i + 1; j < N; j++)
		{
			l[j][i] = a[j][i] / u[i][i];
			u[i][j] = a[i][j];
		}
		for (size_t j = i + 1; j < N; j++)
			for (size_t k = i + 1; k < N; k++)
				a[j][k] -= l[j][i] * u[i][k];
	}
}
int main()
{
	vector<vector<float>>  l(N, vector<float>(N));
	vector<vector<float>>  u(N, vector<float>(N));
	float a[N][N] = {
		2, 3, 1, 5,
		6, 13, 5, 19,
		2, 19, 10, 23,
		4, 10, 11, 31
	};

	lu(l, u, a);

	cout << "L 矩阵:" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
			cout << l[i][j] << '\t';
		cout << endl;
	}
	cout << "U 矩阵:" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
			cout << u[i][j] << '\t';
		cout << endl;
	}

	memset(a, 0, sizeof(a));

	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			for (int k = 0; k < N; k++)
				a[i][j] += l[i][k] * u[k][j];

	cout << "A 矩阵:" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
			cout << a[i][j] << '\t';
		cout << endl;
	}
	return 0;
}

LU分解算法分析
#

前面说到对于一个非奇异矩阵 \( A \),即 \( \det{A} \neq 0 \),就能找到其 \( LU \) 分解,那么运用正向替换与反向替换就可以求出线性方程组 \( Ax = b \) 的解。

原理:算法利用高斯消元法来创建 \( LU \) 分解。首先从其他方程中减去第一个方程的倍数,以把那些方程中的第一个变量消去。然后,从第三个及以后的方程中减去第二个方程的倍数,把这些方程的第一个和第二个变量消去。继续上述过程,直到系统变为一个上三角矩阵形式,实际上此矩阵就是 \( U \)。矩阵 \( L \) 是由消去变量所用的行的乘数组成。

采用递归算法实现这个策略。我们希望构造出一个 \( n \times n \) 的非奇异矩阵 \( A \) 的一个 \( LU \) 分解。 如果 \( n = 1 \),则构造完成,因为可以算则 \( L = I_1 \) (注:\( I_n \) 是单位阵), \( U = A \)。对于 \( n > 1 \),我们把 \( A \) 拆成 \( 4 \) 部分

$$ \begin{bmatrix} a_{11} & | & a_{12} & \cdots & a_{1n} \\ -&-&-&-&- \\ a_{21} & | & a_{22} & \cdots & a_{2n} \\ \vdots & | \\ a_{n1} & | & a_{n2} & \cdots & a_{nn} \end{bmatrix} = \begin{bmatrix} a_{11} & \omega^T \\ \upsilon & A’ \end{bmatrix} $$

其中 \( \upsilon \) 是一个 \( n - 1 \) 维列向量,\( \omega^T \) 是一个 \( n - 1 \) 维行向量,\( A’ \) 是一个 \( (n - 1) \times (n - 1) \) 矩阵。然后,利用矩阵代数 (通过简单地从头到尾使用乘法来验证方程式),可以把 \( A \) 分解为

$$ \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{11}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{11} & \omega^T \\ 0 & A’ - \frac{\upsilon\omega^T}{a_{11}} \end{bmatrix} $$

项 \( \frac{\upsilon\omega^T}{a_{11}} \) 是一个 \( (n - 1) \times (n - 1) \) 矩阵,它与矩阵 \( A’ \) 大小一致。所得矩阵

$$ A’ - \frac{\upsilon\omega^T}{a_{11}} $$

称为矩阵 \( A \) 对于 \( a_{11} \) 的 舒尔补

如果矩阵 \( A \) 是非奇异的,那么舒尔补矩阵也是非奇异的。

因为舒尔补是非奇异的,现在我们可以递归地找出它的一个 \( LU \) 分解。我们说

$$ A’ - \frac{\upsilon\omega^T}{a_{11}} = L’U’ $$

其中 \( L’ \) 是单位下三角矩阵,\( U’ \) 是上三角矩阵。然后,利用矩阵代数可得

$$ \begin{aligned} A &= \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{00}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{00} & \omega^T \\ 0 & A’ - \frac{\upsilon\omega^T}{a_{00}} \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{00}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{k0} & \omega^T \\ 0 & L’U’ \end{bmatrix} \\ &= \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{00}} & L’ \end{bmatrix} \begin{bmatrix} a_{00} & \omega^T \\ 0 & U’ \end{bmatrix} = LU \end{aligned} $$

  • 时间复杂度 \( O(N^3) \)

LUP分解算法实现
#

示例:

$$ \begin{bmatrix} 2 & 0 & 2 & 0.6 \\ 3 & 3 & 4 & -2 \\ 5 & 5 & 4 & 2 \\ -1 & -2 & 3.4 & -1 \end{bmatrix} $$

#include <iostream>
#include <vector>
const int N = 4;
using namespace std;
void lup(vector<float> &P, float a[][N])
{
	for (size_t i = 0; i < N; i++)
	{
		float p = 0; size_t i_;
		for (size_t j = i; j < N; j++)
			if (fabsf(a[j][i]) > p)
				p = fabsf(a[j][i]), i_ = j;
		if (p == 0) return;
		swap(P[i], P[i_]);
		for (size_t j = 0; j < N; j++)
			swap(a[i][j], a[i_][j]);
		for (size_t j = i + 1; j < N; j++)
		{
			a[j][i] /= a[i][i];
			for (size_t k = i + 1; k < N; k++)
				a[j][k] -= a[j][i] * a[i][k];
		}
	}
}
int main()
{
	vector<float> P(N);
	float b[][N] = {
		2, 0, 2, 0.6,
		3, 3, 4, -2,
		5, 5, 4, 2,
		-1, -2, 3.4, -1
	};

    lup(P, b);

	cout << "B 矩阵:" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
			cout << b[i][j] << '\t';
		cout << endl;
	}
    cout << "P 矩阵:" << endl;
	for (size_t i = 0; i < P.size(); i++)
		cout << i << P[i] << endl;
	return 0;
}

版本 2.

void lup2(vector<vector<float>>&l, vector<vector<float>>&u, vector<float> &P, float a[][N])
{
	for (int i = 0; i < N; i++)
	{
		l[i][i] = 1;
		for (int j = i + 1; j < N; j++)
			l[i][j] = 0;
	}
	for (int i = 1; i < N; i++)
		for (int j = 0; j < i; j++)
			u[i][j] = 0;
	for (int i = 0; i < N; i++)
	{
		float p = 0; int i_;
		for (int j = i; j < N; j++)
			if (fabsf(a[j][i]) > p)
				p = fabsf(a[j][i]), i_ = j;
		if (p == 0) return;
		swap(P[i], P[i_]);
		for (int j = 0; j < N; j++)
			swap(a[i][j], a[i_][j]);
		u[i][i] = a[i][i];
		for (int j = i + 1; j < N; j++)
		{
			l[j][i] = a[j][i] / u[i][i];
			u[i][j] = a[i][j];
		}
		for (int j = i + 1; j < N; j++)
			for (int k = i + 1; k < N; k++)
				a[j][k] -= l[j][i] * u[i][k];
	}
}

LUP分解算法分析
#

原理:算法考虑每次计算过程,若主元为 \( 0 \) 或主元的值大于除数时,我们将找到合适的行与当前主元所在的行进行交换。比如,第 \( 1 \) 行,第 \( 1 \) 列为 \( 0 \),我们把第 \( 1 \) 行与第 \( k \) 行互换,这等价于用一个置换矩阵 \( Q \) 左乘矩阵 \( A \)。因此可以把 \( QA \) 写成 \( QA = \begin{bmatrix} a_{k1} & \omega^T \\ \upsilon & A’ \end{bmatrix} \),其中 \( \upsilon = (a_{21}, a_{31}, \cdots, a_{n1})^T, \omega^T = (a_{k2}, a_{k3}, \cdots, a_{kn}) \)

\( A’ \) 是一个 \( (n - 1) \times (n - 1) \) 矩阵。因为 \( a_{k1} \neq 0 \),现在可以执行与 \( LU \) 分解基本相同的线性代数运算,但现在能保证不会除以 \( 0 \)

$$ QA = \begin{bmatrix} a_{k1} & \omega^T \\ \upsilon & A’ \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{k1}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & A’ - \frac{\upsilon\omega^T}{a_{k1}} \end{bmatrix} $$

如果 \( A \) 是非奇异的,那么舒尔补 \( A’ - \frac{\upsilon\omega^T}{a_{k1}} \) 也是非奇异的。因此,可以递归地找出它的一个 \( LUP \) 分解,包括单位下三角矩阵 \( L’ \)、上三角矩阵 \( U’ \) 和置换矩阵 \( P’ \),满足

$$ P’(A’ - \frac{\upsilon\omega^T}{a_{k1}}) = L’U’ $$

定义 $$ P = \begin{bmatrix} 1 & 0 \\ 0 & P’ \end{bmatrix} Q $$

它是一个置换矩阵,因为它是两个置换矩阵的乘积。有

$$ \begin{aligned} PA &= \begin{bmatrix} 1 & 0 \\ 0 & P’ \end{bmatrix} QA = \begin{bmatrix} 1 & 0 \\ 0 & P’ \end{bmatrix} \begin{bmatrix} 1 & 0 \\ \frac{\upsilon}{a_{k1}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & A’ - \frac{\upsilon\omega^T}{a_{k1}} \end{bmatrix} \\ &= \begin{bmatrix} 1 & 0 \\ P’\frac{\upsilon}{a_{k1}} & P’ \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & A’ - \frac{\upsilon\omega^T}{a_{k1}} \end{bmatrix} \\ &= \begin{bmatrix} 1 & 0 \\ P’\frac{\upsilon}{a_{k1}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & P’(A’ - \frac{\upsilon\omega^T}{a_{k1}}) \end{bmatrix} \\ &= \begin{bmatrix} 1 & 0 \\ P’\frac{\upsilon}{a_{k1}} & I_{n - 1} \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & L’U’ \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ P’\frac{\upsilon}{a_{k1}} & L’ \end{bmatrix} \begin{bmatrix} a_{k1} & \omega^T \\ 0 & U’ \end{bmatrix} = LU \end{aligned} $$

这样就推出了 \( LUP \) 分解。因为 \( L’ \) 是单位下三角矩阵,所以 \( L \) 也是单位下三角矩阵;又因为 \( U’ \) 是上三角矩阵,于是 \( U \) 也是上三角矩阵。

  • 时间复杂度 \( O(N^3) \)

求解线性方程组算法
#

有了以上基础知识以后,我们才能得到 \( L, U, P \),步入最后一步求解出线性方程组 \( Ax = b \) 的向量解。

完整的算法实现
#

#include <iostream>
#include <vector>
const int N = 3;
using namespace std;
void lu(vector<vector<float>> &l, vector<vector<float>> &u, float a[][N])
{
	for (int i = 0; i < N; i++)
	{
		l[i][i] = 1;
		for (int j = i + 1; j < N; j++)
			l[i][j] = 0;
	}
	for (int i = 1; i < N; i++)
		for (int j = 0; j < i; j++)
			u[i][j] = 0;
	for (int i = 0; i < N; i++)
	{
		u[i][i] = a[i][i];
		for (int j = i + 1; j < N; j++)
		{
			l[j][i] = a[j][i] / u[i][i];
			u[i][j] = a[i][j];
		}
		for (int j = i + 1; j < N; j++)
			for (int k = i + 1; k < N; k++)
				a[j][k] -= l[j][i] * u[i][k];
	}
}
void lup(vector<float> &P, float a[][N])
{
	for (int i = 0; i < N; i++)
	{
		float p = 0; int i_;
		for (int j = i; j < N; j++)
			if (fabsf(a[j][i]) > p)
				p = fabsf(a[j][i]), i_ = j;
		if (p == 0) return;
		swap(P[i], P[i_]);
		for (int j = 0; j < N; j++)
			swap(a[i][j], a[i_][j]);
		for (int j = i + 1; j < N; j++)
		{
			a[j][i] /= a[i][i];
			for (int k = i + 1; k < N; k++)
				a[j][k] -= a[j][i] * a[i][k];
		}
	}
}
void lup2(vector<vector<float>>&l, vector<vector<float>>&u, vector<float> &P, float a[][N])
{
	for (int i = 0; i < N; i++)
	{
		l[i][i] = 1;
		for (int j = i + 1; j < N; j++)
			l[i][j] = 0;
	}
	for (int i = 1; i < N; i++)
		for (int j = 0; j < i; j++)
			u[i][j] = 0;
	for (int i = 0; i < N; i++)
	{
		float p = 0; int i_;
		for (int j = i; j < N; j++)
			if (fabsf(a[j][i]) > p)
				p = fabsf(a[j][i]), i_ = j;
		if (p == 0) return;
		swap(P[i], P[i_]);
		for (int j = 0; j < N; j++)
			swap(a[i][j], a[i_][j]);
		u[i][i] = a[i][i];
		for (int j = i + 1; j < N; j++)
		{
			l[j][i] = a[j][i] / u[i][i];
			u[i][j] = a[i][j];
		}
		for (int j = i + 1; j < N; j++)
			for (int k = i + 1; k < N; k++)
				a[j][k] -= l[j][i] * u[i][k];
	}
}
void lup_solve(vector<vector<float>>&l, vector<vector<float>>&u, vector<float>&x, vector<float>&y, vector<float> b, vector<float> P, float a[][N])
{
	for (int i = 0; i < N; i++)
	{
		float sum = 0;
		for (int j = 0; j < i; j++)
			sum += l[i][j] * y[j];
		y[i] = b[P[i]] - sum;
	}
	for (int i = N - 1; i >= 0; i--)
	{
		float sum = 0;
		for (int j = i + 1; j < N; j++)
			sum += u[i][j] * x[j];
		x[i] = (y[i] - sum) / u[i][i];
	}
}
int main()
{
	vector<vector<float>>  l(N, vector<float>(N));
	vector<vector<float>>  u(N, vector<float>(N));
	/*float a[][N] = {
		2, 3, 1, 5,
		6, 13, 5, 19,
		2, 19, 10, 23,
		4, 10, 11, 31
	};
	float b[][N] = {
		2, 0, 2, 0.6,
		3, 3, 4, -2,
		5, 5, 4, 2,
		-1, -2, 3.4, -1
	};*/
	float c[][N] = {
		1, 2, 0,
		3, 4, 4,
		5, 6, 3
	};
	vector<float> P(N);
	vector<float> x(N);
	vector<float> y(N);
	vector<float> b { 3, 7, 8 };

	for (int i = 0; i < N; i++)
		P[i] = (float)i;

	lup2(l, u, P, c);
	
	lup_solve(l, u, x, y, b, P, c);

	cout << "解向量 x:" << endl;
	for (int i = 0; i < N; i++)
		cout << x[i] << endl;
	return 0;
}

示例:

$$ \begin{bmatrix} 1 & 2 & 0 \\ 3 & 4 & 4 \\ 5 & 6 & 3 \end{bmatrix} $$

输出:

使用 lup2() 函数

解向量 x:
-10.08
7.94
1.92

算法分析
#

显然,代码很直观的实现了对算法的描述过程,即利用正向替换,代换出 \( Ly = Pb \) 的 \( y \),然后再利用反向替换计算出 \( Ux = y \) 中的解向量 \( x \)。

  • lup_solve()的时间复杂度为 \( O(N^2) \)

矩阵求逆
#

\( A’ \) 是 \( A \) 的逆矩阵,则有 \( AA’ = E \),其中 \( E \) 是单位阵。

设 \( X = A’ \), \( X_i \) 表示 \( X \) 的第 \( i \) 列, \( e_i \) 是 \( E \) 的第 \( i \) 列。于是可以利用 \( A \) 的 \( LUP \) 分解求解方程中的 \( X \),需分别求解每一个方程

$$ AX_i = e_i $$

中的 \( X_i \)。一旦得到 \( LUP \) 分解,就可以在 \( O(N^2) \) 时间内计算 \( n \) 个 \( X_i \) 列中的每一个,因此可以在 \( O(N^3) \) 时间内从 \( A \) 的 \( LUP \) 分解计算 \( X \)。既然可以在 \( O(N^3) \) 内确定出 \( A \) 的 \( LUP \) 分解,我们就可以在 \( O(N^3) \) 的时间内求矩阵 \( A \) 的逆矩阵 \( A’ \)。

算法演示
#

示例:

$$ \begin{bmatrix} 1 & 2 & 0 \\ 3 & 4 & 4 \\ 5 & 6 & 3 \end{bmatrix} $$

int main()
{
	vector<vector<float>>  l(N, vector<float>(N));
	vector<vector<float>>  u(N, vector<float>(N));
	vector<float> x(N);
	vector<float> y(N);
	vector<float> P(N);
	vector<vector<float>> E {
		vector<float>{ 1, 0, 0 }, 
		vector<float>{ 0, 1, 0 },
		vector<float>{ 0, 0, 1}
	};
    float a[][N] = {
		1, 2, 0,
		3, 4, 4,
		5, 6, 3
	};

	lup2(l, u, P, a);
	
	for (int j = 0; j < E.size(); j++)
	{
		lup_solve(l, u, x, y, E[j], P, a);
		cout << "解向量 X[" << j << "]:" << endl;
		for (int i = 0; i < N; i++)
			cout << x[i] << endl;
	}
	return 0;
}

输出:

解向量 X[0]:
-1.2
1.1
-0.2
解向量 X[1]:
-0.6
0.3
0.4
解向量 X[2]:
0.8
-0.4
-0.2

即 $$ A’=\begin{bmatrix} -1.2 & -0.6 & 0.8 \\ 1.1 & 0.3 & -0.4 \\ -0.2 & 0.4 & -0.2 \end{bmatrix} $$

参考文献
#

相关文章

牛顿法(Newton's method)
·961 字·2 分钟· loading · loading
Algorithms Mathematical C/C++ Math
牛顿法也是数值分析中很常见的算法了。嘛,网上对它的各种介绍也
梯度下降算法(更新于 2020/12/04)
·1749 字·4 分钟· loading · loading
Algorithms Math C/C++ MatLab Python
梯度下降法(Gradient descent)又叫最速下降法,
Kruskal's algorithm
·456 字·1 分钟· loading · loading
Algorithms C/C++
Kruskal算法可用来求解最小生成树(minimum-sp
Tarjan's algorithm
·950 字·2 分钟· loading · loading
Algorithms C/C++
Tarjan算法可以用来求有向图的强连通分量个数。算法由Ro
数学-导数篇(持续更新)
·1794 字·4 分钟· loading · loading
Mathematical Math
这里主要存放一些导数题。 题目 # 1.已知向量 \( a = (\sin(x), \frac{3}{4}), b = (\cos(x), -1)
高等数学-积分方程篇(持续更新)
·631 字·2 分钟· loading · loading
Mathematical Math
这里存放一些积分方程题的题解。 题目 # 1.求 \( \int_{0}^{\frac{\pi}{4}} x \ \prod cos(\frac{x}{2^k}) dx \) (from