对于固定维密集线性系统(N = 9)的快速求解,你会推荐哪种算法(矩阵是对称的,正半定的)?
类型是32位和64位浮点。
这样的系统将被解决数百万次,因此算法在维度方面应该相当快(n = 9)。
P.S。我们赞赏提出的算法的健壮的 C ++实现的例子。
1)“解决了数百万次”是什么意思?相同的系数矩阵与一百万个不同的右手术语,或一百万个不同的矩阵?
百万个不同的矩阵。
2)正_semi_definite意味着矩阵可以是单数(对机器精度)。你想怎么处理这个案子?只是提出错误,或者试着回答一些明智的答案?
提出错误是可以的。
答案 0 :(得分:7)
矩阵是对称的,正半定的, Cholesky分解严格优于LU分解。 (大约是LU的两倍,无论矩阵的大小如何。来源:"数值线性代数"由Trefethen和Bau提供)
它实际上也是小密集矩阵的标准(来源:我在计算数学方面有博士学位)迭代方法效率低于直接方法,除非系统变得足够大(快速的经验法则意味着什么,但是总是很高兴:在任何现代计算机上,任何小于100 * 100的矩阵绝对是一个需要直接方法而不是迭代方法的小矩阵。
现在,我建议不要自己动手。有大量优秀的图书馆已经过全面测试。但如果我不得不推荐你一个,那就是Eigen:
顺便说一句,here in the documentation,你在一个漂亮,简洁的表格中有7种直接线性求解器的各种优缺点。看来在你的情况下,LDLT(Cholesky的变种)获胜
答案 1 :(得分:6)
一般来说,最好使用现有的库,而不是自己动手的方法,因为在追求快速,稳定的数值实现时需要注意许多繁琐的细节。
以下是一些可以帮助您入门的内容:
特征库(我个人喜好):
http://eigen.tuxfamily.org/dox/QuickRefPage.html#QuickRef_Headers
犰狳: http://arma.sourceforge.net/
搜索周围,你会发现很多其他人。
答案 2 :(得分:3)
我会推荐LU分解,特别是如果“解决了数百万次”真的意味着“解决一次并应用于数百万载体”。您将创建LU分解,保存它,并对尽可能多的r.h.s应用前向替换。矢量如你所愿。
如果你使用旋转,那么在面对完成时会更稳定。
答案 3 :(得分:2)
LLT或LDLT之间的选择实际上取决于矩阵的条件数,以及您打算如何处理边缘情况。只有当您能够证明在准确性方面具有统计上显着的提高,或者稳健性对您的应用程序至关重要时,才应使用LDLT。
(如果没有你的矩阵样本,很难给出合理的建议,但我怀疑N = 9这么小的顺序,将小对角线项向D的底部转动实际上是没有必要的。所以我会从经典的Cholesky开始,如果相对于某些合理选择的公差,诊断项变小,则简单地中止因子分解。)
Cholesky的代码非常简单,如果你想要一个非常快速的代码,最好自己实现它。
答案 4 :(得分:2)
和上面的其他人一样,我推荐cholesky。我发现增加,减少和内存访问的数量增加意味着LDLt比cholesky慢。
实际上有很多关于cholesky的变体,哪一个最快取决于你为矩阵选择的表示。我通常使用fortran样式表示,即矩阵M是双* M,其中M(i,j)是m [i + dim * j];为此,我认为上三角形的cholesky是(一点点)最快的,就是用U'* U = M寻找上三角形U.
对于固定的,小的维度,绝对值得考虑编写一个不使用循环的版本。一个相对简单的方法是编写一个程序来完成它。我记得,使用一般处理一般情况作为模板的例程,只花了一个上午编写一个编写特定固定维度版本的程序。节省的费用可能相当可观。例如,我的通用版本需要0.47秒来完成一百万个9x9因子,而无环版本需要0.17秒 - 这些时序在2.6GHz个人计算机上运行单线程。
为了表明这不是一项重大任务,我在下面列出了这样一个程序的来源。它包括分解的一般版本作为评论。我已经在矩阵不接近单数的情况下使用了这个代码,我认为它在那里工作正常;然而,对于更精细的工作来说,它可能太粗糙了。
/* ----------------------------------------------------------------
** to write fixed dimension ut cholesky routines
** ----------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <strings.h>
/* ----------------------------------------------------------------
*/
#if 0
static inline double vec_dot_1_1( int dim, const double* x, const double* y)
{
double d = 0.0;
while( --dim >= 0)
{ d += *x++ * *y++;
}
return d;
}
/* ----------------------------------------------------------------
** ut cholesky: solve U'*U = P for ut U in P (only ut of P accessed)
** ----------------------------------------------------------------
*/
int mat_ut_cholesky( int dim, double* P)
{
int i, j;
double d;
double* Ucoli;
for( Ucoli=P, i=0; i<dim; ++i, Ucoli+=dim)
{ /* U[i,i] = P[i,i] - Sum{ k<i | U[k,i]*U[k,i]} */
d = Ucoli[i] - vec_dot_1_1( i, Ucoli, Ucoli);
if ( d < 0.0)
{ return 0;
}
Ucoli[i] = sqrt( d);
d = 1.0/Ucoli[i];
for( j=i+1; j<dim; ++j)
{ /* U[i,j] = (P[i,j] - Sum{ k<i | U[k,i]*U[k,j]})/U[i,i] */
P[i+j*dim] = d*(P[i+j*dim] - vec_dot_1_1( i, Ucoli, P+j*dim));
}
}
return 1;
}
/* ----------------------------------------------------------------
*/
#endif
/* ----------------------------------------------------------------
**
** ----------------------------------------------------------------
*/
static void write_ut_inner_step( int dim, int i, int off)
{
int j, k, l;
printf( "\td = 1.0/P[%d];\n", i+off);
for( j=i+1; j<dim; ++j)
{ k = i+j*dim;
printf( "\tP[%d] = d * ", k);
if ( i)
{ printf( "(P[%d]", k);
printf( " - (P[%d]*P[%d]", off, j*dim);
for( l=1; l<i; ++l)
{ printf( " + P[%d]*P[%d]", l+off, l+j*dim);
}
printf( "));");
}
else
{ printf( "P[%d];", k);
}
printf( "\n");
}
}
static void write_dot( int n, int off)
{
int i;
printf( "P[%d]*P[%d]", off, off);
for( i=1; i<n; ++i)
{ printf( "+P[%d]*P[%d]", off+i, off+i);
}
}
static void write_ut_outer_step( int dim, int i, int off)
{
printf( "\td = P[%d]", off+i);
if ( i)
{ printf( " - (");
write_dot( i, off);
printf( ")");
}
printf( ";\n");
printf( "\tif ( d <= 0.0)\n");
printf( "\t{\treturn 0;\n");
printf( "\t}\n");
printf( "\tP[%d] = sqrt( d);\n", i+off);
if ( i < dim-1)
{ write_ut_inner_step( dim, i, off);
}
}
static void write_ut_chol( int dim)
{
int i;
int off=0;
printf( "int\tut_chol_%.2d( double* P)\n", dim);
printf( "{\n");
printf( "double\td;\n");
for( i=0; i<dim; ++i)
{ write_ut_outer_step( dim, i, off);
printf( "\n");
off += dim;
}
printf( "\treturn 1;\n");
printf( "}\n");
}
/* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
**
** ----------------------------------------------------------------
*/
static int read_args( int* dim, int argc, char** argv)
{
while( argc)
{ if ( strcmp( *argv, "-h") == 0)
{ return 0;
}
else if (strcmp( *argv, "-d") == 0)
{ --argc; ++argv;
*dim = atoi( (--argc, *argv++));
}
else
{ break;
}
}
return 1;
}
int main( int argc, char** argv)
{
int dim = 9;
if( read_args( &dim, --argc, ++argv))
{ write_ut_chol( dim);
}
else
{ fprintf( stderr, "usage: wchol (-d dim)? -- writes to stdout\n");
}
return EXIT_SUCCESS;
}
/* ----------------------------------------------------------------
*/
答案 5 :(得分:0)
由于易于使用,您可以将Eigen解算器用于比较。对于特定用例,特定解算器可能更快,但另一个应该更好。为此,您可以仅为选择来测量每个算法的运行时间。之后,您可以实现所需的选项(或找到最符合您需求的现有选项)。