用GSL实现C语言的高效双积分
考虑二重积分用GSL实现C语言的高效双积分,c,memory,C,Memory,考虑二重积分 I = int int [(a^k)*b] da db 我们想要在[0,1]和[0,1]和k之间积分的a是一个常数。我正在使用GSL数值积分库,但内存分配有问题 我的代码如下 #include <stdlib.h> #include <stdlib.h> #include <math.h> #include <gsl/gsl_integration.h> double innerIntegrand(double a, void *
I = int int [(a^k)*b] da db
我们想要在[0,1]
和[0,1]
和k
之间积分的a
是一个常数。我正在使用GSL数值积分库,但内存分配有问题
我的代码如下
#include <stdlib.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_integration.h>
double innerIntegrand(double a, void *params) {
double *cast_params = (double *) params;
double b = params[0];
double k = params[1];
return pow(a,k)*b;
}
double outerIntegrand(double b, void *params) {
// params = {holder for double b, k}
double *cast_params = (double *) params;
cast_params[0] = b;
// Allocate integration workspace
gsl_integration_workspace *giw = gsl_integration_workspace_alloc(100);
// Create GSL function
gsl_function F;
F.function = &innerIntegrand;
F.params = params;
// Initialise values to put the result in
double result;
double abserror;
// Perform integration
gsl_integration_qag(&F, 0, 1, 0.001, 0.001, 100, 1, giw, &result, &abserror);
// Free the integration workspace
gsl_integration_workspace_free(giw);
// Return result
return result
}
但是请注意,我必须在函数中分配和释放集成工作区。这意味着在评估最终积分函数时会执行多次
double Integral(double k) {
// Create params
double *params = malloc(2*sizeof(double));
params[1] = k;
// Allocate integration workspace
gsl_integration_workspace *giw = gsl_integration_workspace_alloc(100);
// Create GSL function
gsl_function F;
F.function = &outerIntegrand;
F.params = params;
// Initialise values to put the result in
double result;
double abserror;
// Perform integration
gsl_integration_qag(&F, 0, 1, 0.001, 0.001, 100, 1, giw, &result, &abserror);
// Free the integration workspace
gsl_integration_workspace_free(giw);
// Free memory
free(params);
// Return result
return result
}
理想情况下,我需要两个全局gsl\u integration\u workspace
变量,一个用于outerIntegrand
中的积分,另一个用于integral
中的积分。但是,当我试图将它们声明为全局值时,我收到一个初始值设定项元素不是常量错误
有人能想出一种方法来做这个二重积分而不需要重复的内存分配和释放吗?我想我们也可以通过 PARAMS 参数传递工作空间,尽管它开始变得相当混乱。< P>我在C++中建立了一个外观很好的程序,用于基于GSL的双重集成,避免以干净的方式重复分配。我使用了这个众所周知的函数:
f(x,y)=exp(-x*x-y*y)
在整个平面上对其进行积分(通过切换到极坐标可以很容易地获得结果,pi
)。
通过lambda捕获对其进行修改并添加参数非常简单
#include <iostream>
#include <gsl/gsl_integration.h>
// Simple RAII wrapper
class IntegrationWorkspace {
gsl_integration_workspace * wsp;
public:
IntegrationWorkspace(const size_t n=1000):
wsp(gsl_integration_workspace_alloc(n)) {}
~IntegrationWorkspace() { gsl_integration_workspace_free(wsp); }
operator gsl_integration_workspace*() { return wsp; }
};
// Build gsl_function from lambda
template <typename F>
class gsl_function_pp: public gsl_function {
const F func;
static double invoke(double x, void *params) {
return static_cast<gsl_function_pp*>(params)->func(x);
}
public:
gsl_function_pp(const F& f) : func(f) {
function = &gsl_function_pp::invoke; //inherited from gsl_function
params = this; //inherited from gsl_function
}
operator gsl_function*(){return this;}
};
// Helper function for template construction
template <typename F>
gsl_function_pp<F> make_gsl_function(const F& func) {
return gsl_function_pp<F>(func);
}
int main() {
double epsabs = 1e-8;
double epsrel = 1e-8;
size_t limit = 100;
double result, abserr, inner_result, inner_abserr;
IntegrationWorkspace wsp1(limit);
IntegrationWorkspace wsp2(limit);
auto outer = make_gsl_function( [&](double x) {
auto inner = make_gsl_function( [&](double y) {return exp(-x*x-y*y);} );
gsl_integration_qagi(inner, epsabs, epsrel, limit, wsp1,
&inner_result, &inner_abserr);
return inner_result;
} );
gsl_integration_qagi(outer, epsabs, epsrel, limit, wsp2, &result, &abserr);
std::cout << result << std::endl;
}
#包括
#包括
//简单RAII包装器
类集成工作区{
gsl_集成_工作空间*wsp;
公众:
集成工作区(常数大小n=1000):
wsp(gsl_集成_工作空间_分配(n)){}
~IntegrationWorkspace(){gsl_integration_workspace_free(wsp);}
运算符gsl_集成_工作区*(){return wsp;}
};
//从lambda构建gsl_函数
模板
类gsl_函数\u pp:公共gsl_函数{
常数F func;
静态双调用(双x,void*params){
返回static_cast(params)->func(x);
}
公众:
gsl_函数_pp(常数F&F):func(F){
function=&gsl\u function\u pp::invoke;//从gsl\u函数继承
params=this;//从gsl_函数继承
}
运算符gsl_函数*(){返回此;}
};
//模板构造的辅助函数
模板
gsl_函数\u pp生成\u gsl_函数(常数F&func){
返回gsl_函数_pp(func);
}
int main(){
双epsabs=1e-8;
双epsrel=1e-8;
尺寸限制=100;
双结果,abserr,内结果,内结果abserr;
集成工作空间wsp1(限制);
集成工作空间wsp2(限制);
自动外部=生成gsl功能([&](双x){
自动内部=make_gsl_函数([&](双y){返回exp(-x*x-y*y);});
gsl_集成_qagi(内部、epsabs、epsrel、极限、wsp1、,
&内部搜索结果和内部搜索结果);
返回内部结果;
} );
gsl_集成_qagi(外部、epsabs、epsrel、限制、wsp2、结果和abserr);
这看起来很奇怪:
double innerIntegrand(double a, void *params) {
double *cast_params = (double *) params;
double b = params[0];
double k = params[1];
期望(void*)参数[0]和[1]正确映射到双精度b和k是否正确?如何计算void和双精度类型之间的适当偏移量
这里有一些提示(不要期望下面的工作代码)。
您可以尝试以下方法:
double b = (double )*param;
double k = (double )*(param + sizeof(double));
但也许宣布:
double Integral(double k) {
struct p {
double b;
double k;
} params;
params.k = k;
...
gsl_function F;
F.function = &outerIntegrand;
F.params = ¶ms;
...
double outerIntegrand(double b, void *params) {
(struct p)params->b = b;
double innerIntegrand(double a, void *params) {
double b = (struct p)params->b;
double k = (struct p)params->k;
您可能需要定义“struct p”。您的所有分配似乎都是常量大小。为什么不只声明一个数组而不是malloc()
或新建
-ing?或者,以不太理想的方式,在调用者中进行分配,并将其传递给被调用者。分配是非常昂贵的操作,除非绝对必要,否则不应在紧循环中执行。这听起来是个好主意,尽管我不知道有任何其他方法来声明gsl_集成_工作区
ob没有分配功能的对象。是否有一种简单的方法来代替数组执行此操作?您提到了内存分配问题。您能提供更多有关此问题的详细信息吗?是100(size\u t
)的空间吗元素还不够?同样,根据这一点,您可以多次使用同一个工作区。问题是我正在多次分配和释放工作区。正如链接所述,一个工作区可以多次使用,但我无法确定如何将该工作区传递给每个内部集成(显然,外部集成将需要一个不同的工作区)。如何使用类型void*
将这些变量声明为全局变量,然后在函数中显式地键入强制转换它们?我想知道这是否可行?