未确定方程系统的C ++库

时间:2011-10-17 20:18:38

标签: c++ solver equation-solving

我一直在寻找一个用c ++编写的库来解决像这样的未确定的系统

q是向量,w,x,y,z变量和a,b,c,d常数。

argmin_q MAX(q) - MIN(q)

s.t。

q[1] = a - w - y
q[2] = b - w - z
q[3] = c - x - y
q[4] = d - x - z 

找到求解器,算法等非常有用。我发现有几个库能够解决未确定的系统,但另外我需要最小化系数之间的距离。

提前谢谢

altober

2 个答案:

答案 0 :(得分:2)

虽然你可能没有意识到这一点,但你的问题可能更多的是线性代数问题,而不是编程问题,尽管它确实有编程维度。

在这个答案中,我假设你的实际问题可能比你在插图中提出的N = 8草图大得多。首先是数学讨论。示例代码如下。

线性代数问题的本质似乎是它们要么通过一般的线性代数技术实际上可以治疗,要么它们不是。自20世纪60年代以来,L-U因子分解和奇异值分解等一般线性代数技术得到了彻底的发展,并具有坚实的理论基础。麻烦的是这种通用技术在计算处理中倾向于O(N * N * N),也就是说使你的矢量长10倍需要1000倍的计算机时间来治疗。除了一些最大的实际大小,你的问题在计算上变得难以处理 - 至少通过一般的线性代数技术。

如果您的问题极其严重

幸运的是,存在令人眼花缭乱的各种专用技术,以将O(N * N * N)问题减少为O(N * N)问题。自20世纪60年代以来,这些技术大多出现,并且是一个积极研究的领域。不幸的是,要知道应用哪种技术以及如何应用,需要对问题进行深入理解并对矩阵数学进行相当好的掌握。在O(N * N * N)处,已知一般技术。在O(N * N)处,没有通用技术,只有许多专用技术适用于受限制的系统。如果你的工作时间足够长,你通常可以找到一个合适的限制,但它绝不像将矩阵转储到某些通用求解器那么简单。不在O(N * N)。

我读过的关于这个主题的最好的书是Henk A. van der Vorst的大型线性系统的迭代Krylov方法。很高兴,这本书的价格是合理的。当然,在处理van der Voorst之前,您需要在一般线性代数技术方面有一个坚实的基础。我建议乔尔·N·富兰克林1968年的短篇小说 Matrix Theory,以廉价的1993年平装本来自多佛。 (范德沃斯特并没有提到卡普和布朗的聪明,简单且经常有效的有序多重交互方法,我一再认为这很有用,所以让我在这里提一下,这样你就可以了如果你需要的话,请查阅。披露:我个人认识布朗并且可能因为这个原因而偏袒;但是,他和Kapp的技术仍然很好,范德沃斯特省略了。)

由于我不完全理解的原因,大多数最近的技术(虽然不是卡普和布朗)似乎更喜欢以实际数字工作,将复数作为特例处理或忽略它们完全。你没有提到你的数字是否复杂,但如果它们是,那么这可能会在某种程度上限制你的选择。

作为富兰克林和范德沃斯特之间的中间水平,如果你没有时间或兴趣去解决后者,你应该至少抬头并熟悉共轭梯度的方法。

如果你的问题只是中等大小

如果你的问题只是中等大小 - 比如,N< 20,000左右 - 那么你的任务就容易多了。在这种情况下忘记范德沃斯特:你不需要他。根据您是否想要以毫秒,秒,分钟或小时为单位的答案(您还没有提到它,但它只影响N的实际限制),您可以容忍O(N * N * N)性能,并且在这种情况下,富兰克林从20世纪60年代开始的一般技术非常强大。

快速,高效,彻底和正确,LAPACK库及其低级助手BLAS是该领域的标准。你应该使用它们。

LAPACK,BLAS和C ++

我的一个人和我各自尝试了各种C和C ++接口到LAPACK和BLAS。出于各种原因,我们发现它们都没有完全令人满意,尽管它们确实试图提供帮助。我们每个人最终决定完全跳过界面并直接使用LAPACK和BLAS。这是我倾向于向你推荐的。

LAPACK和BLAS旨在从FORTRAN 77调用,而不是从C ++调用。尽管如此,用C ++调用它们并不是那么难,而且你不需要C ++接口来完成它。事实上,你可能不想要一个C ++接口,至少不是其他人准备的通用接口。如果你不让一个人窒息,那么LAPACK和BLAS会更快乐。 (请记住,FORTRAN 77虽然有效,但其链接设施有限,缺少C风格的头文件。)

你必须知道直接使用LAPACK和BLAS的第一件事就是这样,在你理解它之前你会感到很痛苦:矩阵存储在列专业中。也就是说,每列,而不是每一列矩阵的行在存储器中依次表示为一个单元。因此,矩阵元素直接存储在其上方和下方的元素之间,而不是直接存储在其左右元素之间。实际上,LAPACK和BLAS对于以这种方式存储矩阵是谨慎的,因为Kernighan和Ritchie发明了C,对线性代数似乎从未如此感兴趣。 C是一种很好的语言,但是C的默认矩阵存储约定可能是第一个错误。从数学家的角度来看,LAPACK和BLAS以它们应该存储的方式存储矩阵,列为主;并且,如果您正在编写线性代数代码,那么您也应该以这种方式存储矩阵。忽略C的默认,行主要惯例:它与矩阵数学的约定无关,你不需要它。

这是一个完整的例子:

#include <iostream>

extern "C" {
    void dgesv_(
        const int *N,
        const int *NRHS,
        double    *A,
        const int *LDA,
        int       *IPIV,
        double    *B,
        const int *LDB,
        int       *INFO
    );
}

int main()
{

    const int N = 2;

    // Let A = | 3.0 6.1 |
    //         |-1.2 1.7 |
    double A[N*N] = {3.0, -1.2, 6.1, 1.7};

    // Let b = | 5.5 |
    //         | 0.4 |
    double x[N] = {5.5, 0.4};
    // (Why is b named x?  Answer:  because b and x share
    // the same storage, because Lapack will write x over b.)

    // Solve the linear system A*x = b.
    const int NRHS = 1;
    const int LDA  = N;
    int       IPIV  [N];
    const int LDB  = N;
    int       INFO;
    dgesv_(&N, &NRHS, A, &LDA, IPIV, x, &LDB, &INFO);
    // Uncomment the next line if you wish to see
    // the error code Lapack returns.
    //std::cout << "INFO == " << INFO << "\n";

    // Output x.
    for (int i = 0; i < N; ++i) std::cout << x[i] << "\n";

    return 0;

}

[为什么函数名为dgesv_()?答:它不是,至少不在FORTRAN 77中,它被命名为DGESV。但是,FORTRAN 77不区分大小写,当我们将其转换为C的链接约定时,我们得到dgesv_()。至少,这是我的同事和我在我们尝试过的每台Linux,BSD或OSX机器上得到的东西。在Debian或Ubuntu上,shell命令&#34; readelf --symbols /usr/lib/liblapack.so | grep -i DGESV&#34;发现这一点。在Microsoft平台上,C链接符号可能有其他名称:您必须调查这是否与您相关。]

LAPACK和BLAS非常擅长他们的工作。你应该使用它们。

无论是将数据存储在C风格的数组中(如示例所示)还是在C ++中,样式std :: valarrays都取决于您。 LAPACK和BLAS不在乎,只要存储是专栏。

系数的最小化

你没有给我足够的信息来准确说出你想对你的系数做什么,但有人怀疑你可能需要的是N-by(2 * N)欠定系统的解决方案。这样做超出了富兰克林的书的范围,但是,如果你已经吸收了富兰克林的材料,那么my own notes on the pseudoinverse可以帮助你。

显然,如果您正在寻找快速解决方案,我就没有。可能有一个固有的软件包可以完全按照您的要求进行操作,但我的经验表明,在这方面可能性很大。在实践中出现了数百种不同类型的矩阵问题,这些问题在固有软件中破裂。但是,LAPACK和BLAS,以及Franklin,van der Vorst以及您正在阅读的答案,提供了解决特定问题所需的所有工具。

祝你好运。

答案 1 :(得分:0)

您检查了Template Numerical Toolkit吗?或其前身lapack++