有效地求解Ax = b,其中A是4x4对称矩阵,b是4x1向量

时间:2014-05-03 14:26:33

标签: c++ opencv linear

我将解决一个小的线性系统Ax = b,其中A是一个4乘4 对称矩阵,存储了16 double个数字(实际上有10个)足以代表它),b是4乘1的向量。问题是,我必须运行这种系统数百万次。所以我正在寻找最有效的库来解决它。我在cv::solve()中尝试了OpenCV方法,但我仍觉得它很慢。

由于矩阵A是对称的,我记得Conjugate Gradient算法因其效率而可能是一个很好的候选者。但是,我还没有找到它的库(英特尔MKL似乎有一个,但它是专为稀疏矩阵设计的,不适合我的问题)。

有人可以帮我吗?

3 个答案:

答案 0 :(得分:5)

由于矩阵维度是固定的,我认为你最好,直接实现逆。 这项任务有一个现成的公式。你有:

Inverse Matrix formular

B的条目由下式给出: Calculation of the B_ij

两个公式都来自this site

您应该能够利用您的矩阵是对称的事实进一步简化这些条目的计算。如果你这样做,我认为你会比任何一般矩阵逆实现更快。

然后你仍然需要将A^-1应用到你的b,这是一个简单的矩阵向量乘法,你也应该硬编码,以获得最佳性能。

所有这些都假定您确实需要针对此特定问题的最佳性能。如果矩阵维度发生变化,或者这不是算法中最关键的部分,请查看Eigen,Lapack / Blas或任何其他线性代数库。解决密集的线性系统是基本任务,应该包含在任何这样的库中。

答案 1 :(得分:1)

为了上帝的缘故,请不要自己写。 如果我理解正确,那么您正在寻找有效解决密集线性系统的方法。这正是LAPACK的用途。 netlib.org上的版本(请参阅this page以获取有关您应该使用的例程的指导)非常快,但如果您需要一些真正尖叫的东西,请查看MKL,ATLAS或GotoBLAS。

编辑:由于这是一个C ++论坛,我应该补充一点,您可以使用Eigen包进行解决。它将使用其中一个LAPACK例程的一些实现。

答案 2 :(得分:0)

主题可能已经过时了,我解决了类似的问题,这个主题可能对其他人有用。

4维非常小,因此直接计算矩阵求逆的算法是有效的。 Hovewer,如果您只需要一个解决方案的A矩阵,则无需计算整个反演。可以看出,对于逆矩阵的所有项,除以行列式(矩阵求逆中的运算)并不是必需的。您只能划分4个解决方案而不是所有16个矩阵术语。还有一些优化。这是我的4D-solver(c ++代码)

的实现
void getsub(double* sub, double* mat, double* vec)
{
*(sub++) = *(mat  ) * *(mat+5) - *(mat+1) * *(mat+4);
*(sub++) = *(mat  ) * *(mat+6) - *(mat+2) * *(mat+4);
*(sub++) = *(mat  ) * *(mat+7) - *(mat+3) * *(mat+4);
*(sub++) = *(mat  ) * *(vec+1) - *(vec  ) * *(mat+4);
*(sub++) = *(mat+1) * *(mat+6) - *(mat+2) * *(mat+5);
*(sub++) = *(mat+1) * *(mat+7) - *(mat+3) * *(mat+5);
*(sub++) = *(mat+1) * *(vec+1) - *(vec  ) * *(mat+5);
*(sub++) = *(mat+2) * *(mat+7) - *(mat+3) * *(mat+6);
*(sub++) = *(mat+2) * *(vec+1) - *(vec  ) * *(mat+6);
*(sub  ) = *(mat+3) * *(vec+1) - *(vec  ) * *(mat+7);
}

void solver_4D(double* mat, double* vec)
{
    double a[10], b[10]; // values of 20 specific 2D subdeterminants

    getsub(&a[0], mat, vec);
    getsub(&b[0], mat+8, vec+2);

    *(vec++) = a[5]*b[8] + a[8]*b[5] - a[6]*b[7] - a[7]*b[6] - a[4]*b[9] - a[9]*b[4];
    *(vec++) = a[1]*b[9] + a[9]*b[1] + a[3]*b[7] + a[7]*b[3] - a[2]*b[8] - a[8]*b[2];
    *(vec++) = a[2]*b[6] + a[6]*b[2] - a[0]*b[9] - a[9]*b[0] - a[3]*b[5] - a[5]*b[3];
    *(vec  ) = a[0]*b[8] + a[8]*b[0] + a[3]*b[4] + a[4]*b[3] - a[6]*b[1] - a[1]*b[6];

    register double idet = 1./(a[0]*b[7] + a[7]*b[0] + a[2]*b[4] + a[4]*b[2] - a[5]*b[1] - a[1]*b[5]);

    *(vec--) *= idet;
    *(vec--) *= idet;
    *(vec--) *= idet;
    *(vec  ) *= idet;
}

*mat表示指向形式为

的A矩阵的指针
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

*vec表示0 1 2 3形式的惯性向量。方法抛弃惯性矢量并用新的替换它们。

您只需要74次乘法和一次除法。如果您具有SSE优化经验,此方法可能适用于SSE编码器(大多数操作是双重的)。

这个适用于所有可逆A矩阵。您可以将标识a[7] = b[0]用于对称A矩阵。它并不多,但一般四维可逆A矩阵的原始代码仍然很快。