C++ 计算行列式的最快方法是什么?
我正在编写一个库,其中我希望有一些基本的NxN矩阵功能,没有任何依赖关系,这是一个学习项目。我把我的表现和艾根做比较。我已经能够相当平等,甚至击败了它的表现与SSE2和AVX2击败了它在相当多的战线(它只使用SSE2,所以不超级令人惊讶) 我的问题是,我用高斯消去法创建一个上对角化矩阵,然后乘以对角线得到行列式。我在N<300时击败了本征值,但在那之后,本征值把我吹走了,随着矩阵变大,情况变得更糟。考虑到所有内存都是按顺序访问的,而且编译器的解压看起来并不可怕,我认为这不是一个优化问题 可以进行更多的优化,但计时看起来更像是算法计时复杂性问题,或者有一个我没有看到的主要SSE优势。当我尝试这样做时,简单地展开一点循环对我没有多大帮助 计算行列式有更好的算法吗 标量码C++ 计算行列式的最快方法是什么?,c++,algorithm,matrix,linear-algebra,algebra,C++,Algorithm,Matrix,Linear Algebra,Algebra,我正在编写一个库,其中我希望有一些基本的NxN矩阵功能,没有任何依赖关系,这是一个学习项目。我把我的表现和艾根做比较。我已经能够相当平等,甚至击败了它的表现与SSE2和AVX2击败了它在相当多的战线(它只使用SSE2,所以不超级令人惊讶) 我的问题是,我用高斯消去法创建一个上对角化矩阵,然后乘以对角线得到行列式。我在N
/*
Warning: Creates Temporaries!
*/
template<typename T, int ROW, int COLUMN> MML_INLINE T matrix<T, ROW, COLUMN>::determinant(void) const
{
/*
This method assumes square matrix
*/
assert(row() == col());
/*
We need to create a temporary
*/
matrix<T, ROW, COLUMN> temp(*this);
/*We convert the temporary to upper triangular form*/
uint N = row();
T det = T(1);
for (uint c = 0; c < N; ++c)
{
det = det*temp(c,c);
for (uint r = c + 1; r < N; ++r)
{
T ratio = temp(r, c) / temp(c, c);
for (uint k = c; k < N; k++)
{
temp(r, k) = temp(r, k) - ratio * temp(c, k);
}
}
}
return det;
}
/*
警告:创建临时对象!
*/
模板MML_内联T矩阵::行列式(空)常数
{
/*
此方法假定为平方矩阵
*/
断言(行()==col());
/*
我们需要建立一个临时的
*/
矩阵温度(*本);
/*我们将临时形式转换为上三角形式*/
uint N=行();
T det=T(1);
对于(uint c=0;c
AVX2
模板浮点矩阵::行列式(void)常量
{
/*
此方法假定为平方矩阵
*/
断言(行()==col());
/*
我们需要建立一个临时的
*/
矩阵温度(*本);
/*我们将临时形式转换为上三角形式*/
浮点数=1.0f;
常数N=行();
常数Nm8=N-8;
常数Nm4=N-4;
uint c=0;
对于(;c
通过高斯消去法将n×n矩阵简化为上(或下)三角形式的算法通常具有O(n^3)的复杂度(其中^表示“的幂”)
有计算definite的替代方法,例如计算特征值集(方阵的行列式等于其特征值的乘积)。对于一般矩阵,完整特征值集的计算实际上也是-O(n^3)
然而,理论上,特征值集的计算复杂度为
n^w
,其中w介于2和2.376之间-这意味着对于(大得多)的矩阵,它将比使用高斯消去法更快。请看James Demmel、Iona Dumitriu和Olga Holtz在2007年11月的《数字数学》第108卷第1期第59-91页中的一篇文章“快速线性代数是稳定的”。如果Eigen使用复杂度小于O(n^3)的方法来处理更大的矩阵(我不知道,从来没有理由调查这些事情),这将解释您的观察结果。答案大多数地方似乎使用块LU分解在同一内存空间中创建下三角形和上三角形矩阵。它是~O(n^2.5),取决于您使用的块的大小
这是莱斯大学的一个power point,解释了该算法
被矩阵除意味着被其逆矩阵相乘
这个想法似乎是要显著增加n^2操作的数量,但减少m^3的数量,这实际上降低了算法的复杂性,因为m的大小是固定的
要以一种高效的方式来写这篇文章需要一点时间,因为高效地写这篇文章需要我还没有写过的“就地”算法。
template<> float matrix<float>::determinant(void) const
{
/*
This method assumes square matrix
*/
assert(row() == col());
/*
We need to create a temporary
*/
matrix<float> temp(*this);
/*We convert the temporary to upper triangular form*/
float det = 1.0f;
const uint N = row();
const uint Nm8 = N - 8;
const uint Nm4 = N - 4;
uint c = 0;
for (; c < Nm8; ++c)
{
det *= temp(c, c);
float8 Diagonal = _mm256_set1_ps(temp(c, c));
for (uint r = c + 1; r < N;++r)
{
float8 ratio1 = _mm256_div_ps(_mm256_set1_ps(temp(r,c)), Diagonal);
uint k = c + 1;
for (; k < Nm8; k += 8)
{
float8 ref = _mm256_loadu_ps(temp._v + c*N + k);
float8 r0 = _mm256_loadu_ps(temp._v + r*N + k);
_mm256_storeu_ps(temp._v + r*N + k, _mm256_fmsub_ps(ratio1, ref, r0));
}
/*We go Scalar for the last few elements to handle non-multiples of 8*/
for (; k < N; ++k)
{
_mm_store_ss(temp._v + index(r, k), _mm_sub_ss(_mm_set_ss(temp(r, k)), _mm_mul_ss(_mm256_castps256_ps128(ratio1),_mm_set_ss(temp(c, k)))));
}
}
}
for (; c < Nm4; ++c)
{
det *= temp(c, c);
float4 Diagonal = _mm_set1_ps(temp(c, c));
for (uint r = c + 1; r < N; ++r)
{
float4 ratio = _mm_div_ps(_mm_set1_ps(temp[r*N + c]), Diagonal);
uint k = c + 1;
for (; k < Nm4; k += 4)
{
float4 ref = _mm_loadu_ps(temp._v + c*N + k);
float4 r0 = _mm_loadu_ps(temp._v + r*N + k);
_mm_storeu_ps(temp._v + r*N + k, _mm_sub_ps(r0, _mm_mul_ps(ref, ratio)));
}
float fratio = _mm_cvtss_f32(ratio);
for (; k < N; ++k)
{
temp(r, k) = temp(r, k) - fratio*temp(c, k);
}
}
}
for (; c < N; ++c)
{
det *= temp(c, c);
float Diagonal = temp(c, c);
for (uint r = c + 1; r < N; ++r)
{
float ratio = temp[r*N + c] / Diagonal;
for (uint k = c+1; k < N;++k)
{
temp(r, k) = temp(r, k) - ratio*temp(c, k);
}
}
}
return det;
}