【问题标题】:Speed up a C++ code implementing numerical integration加速实现数值积分的 C++ 代码
【发布时间】:2018-07-25 16:00:37
【问题描述】:

我有这个实现矩形数值积分的 C++ 代码

#include <iostream>
#include <cmath>
using namespace std;

float pdf(float u){
     return (1/(pow(1+u, 2)));
}

float cdf(float u){
      return (1 - 1/(u+1));
}
// The main function that implements the numerical integration, 
//and it is a recursive function
float integ(float h, int k, float du){
      float res = 0;
      if (k == 1){
         res =  cdf(h);
      }else{
         float u = 0;
    while (u < h){
        res += integ(h - u, k - 1, du)*pdf(u)*du;
        u += du;
    }
}
     return res;
}
int main(){
    float du = 0.0001;
    int K = 3;
    float gamma[4] = {0.31622777, 0.79432823, 
                1.99526231, 5.01187234};
    int G = 50;
    int Q = 2;
    for (int i = 0; i < 4; i++){
        if ((G-Q*(K-1)) > 0){
            float gammath = (gamma[i]/Q)*(G-Q*(K-1));
            cout<<1-integ(gammath, K, du)<< endl;
    }

 }

    return 0;
}

虽然我从 Python 和 MATLAB 切换到 C++,但我遇到了速度问题,因为 C++ 更快。问题是我需要一个小步长 du 来获得对集成的准确评估。

基本上,我想评估 gammath 定义的 4 个不同点的积分,这是其他定义参数的函数。

无论如何我可以加快这个程序吗?我已经比 Python 中的相同代码获得了 25 倍以上的速度因子,但代码仍然花费了太长时间(我运行了一整夜,早上还没有完成)。这仅适用于 K=3 和 G=50。在其他情况下,我想测试 K = 10,G = 100 或 300。

提前感谢您的任何提示。

【问题讨论】:

  • codereview.stackexchange.com 可能更适合这个问题。
  • 我注意到的一件事是(G-Q*(K-1)) 在循环中,并且在循环的每次迭代中都有两次。将其排除在循环之外可能会有所帮助,但不确定有多少。
  • pow(u, 2) 替换为(u*u)
  • @RSahu 对,(G-Q*(K-1) 必须评估一次,但我认为这不会显着加快我的代码速度。我认为我需要在我的代码中进行一些算法更改可能让我再加速 25 倍。
  • 在呈现的代码中仍然看不到它,不知道/关心它实际实现的算法:所有输入都是按值,唯一的返回是累积的,没有全局变量,但也许这就是 k=2 递归级别中特定 h 的优化/值重用范围在哪里?在 k=3 中,随着 u 增加到 h,k=2 要做的工作会减少,在我看来,k=3 的一些输入在某些递归路径上将是相同的。

标签: c++11 numerical-methods


【解决方案1】:

您的计算背后是您获取 pdf 函数的 K 倍卷积功率,然后将该功率从 0 积分到 h。当您使用黎曼和进行积分时,这意味着您将 pdf 视为步长函数,步长为 du。在这种情况下,卷积幂的值可以计算为(截断的)幂级数/生成函数的幂中的系数

p(z)=pdf(0)+pdf(du)*z+pdf(2*du)*z^2+...+pdf(n*du)*z^n

在哪里n*du&gt;h。您现在可以通过基于 FFT 的算法计算此功率。一个更基本的变体使用 if q(z)=p(z)^K mod z^(n+1) then

p(z)*q'(z) = K*q(z)*p'(z)  mod z^n 

这样q 的系数可以通过对p 的系数p[j]=pdf(j*du) 的卷积求和来计算。比较上式中的幂z^(m-1) 的项,得出系数级别

sum  p[m-j]*j*q[j] = K * sum  q[j]*(m-j)*p[m-j],    j=0..m

或者当之前的系数q[0],...,q[m-1]已经计算出来时,求解新的系数q[m]

q[m] = 1/(m*p[0]) * sum  (K*(m-j)-j)*p[m-j]*q[j],    j=0..m-1

在给出的代码中

q[0] = pow(p[0], K);
for(m=1; m<=n; m++) {
    q[m]=0;
        for(j=0; j<m; j++) { q[m] += (K*(m-j)-j)*p[m-j]*q[j]; }
    q[m] /= m*p[0];
}

然后总结出结果,

res = q[0]; 
for(j=1; j*du < h; j++) { res += q[j]; }
res *= pow(du, K);

【讨论】:

  • 但是在我的问题中不是一维积分,而是K维积分,并且递归是必要的,因为每个维度都取决于之前维度的积分值。上面代码中没有看到参数K。
  • 那么您可能应该写下您想要将pdf(u[1])*...*pdf(u_[n]) 集成到域u[k]&gt;=0u[1]+...+u[k]&lt;=h 上。
  • 很抱歉有任何混淆,但我只想加快程序的速度。我不知道我真正想做的事情很重要。也许我错了。详情请参考this
猜你喜欢
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-01
相关资源
最近更新 更多