GMP给出了一系列扩展的错误结果

时间:2017-03-30 14:46:40

标签: c++ series gmp

我正在使用GMP包来实现两个函数的产品,我将其表达为两个收敛系列的柯西产品。

更详细:我正在寻找一种计算f(x)=g(x)*h(x)的方法,其中g(x)是指数函数,h(x)是一种特殊的超几何函数(见下文),两者都表示为系列

我的问题是,这符合我自己的近似值和wolframalpha对x<29的结果,但对x>29失败了。在实践中,我需要大约x=10^6的值。

我使用的3个公式如下图所示:

formulas

守则

void invfak(mpf_t invn, unsigned int n) //Calculates inverse factorial, !/n!
{
        unsigned int i;
        mpf_t k;

        mpf_init_set_d(k,1.0);
        mpf_init_set_d(invn,0.0);
        i=2;

        for (i = 2; i <= n; ++i) {
            mpf_mul_ui(k, k, i);
        }

        mpf_ui_div(invn,1.0,k);
        mpf_clear(k);
}

 void E2F2E(mpf_t result, long double x, unsigned int N)
{
        mpf_t Q1,Q2;          ///gives Nth term in series expansion of exp(x)

        mpf_init_set_d(Q1,x);
        mpf_init_set_d(Q2,0.0);
        mpf_init_set_d(result,0.0);

        mpf_pow_ui(Q1,Q1,N); /// Q1=Q1^N=x^N
        invfak(Q2,N);       /// Q2=1/N!

        mpf_mul(result,Q1,Q2); ///result= Q1*Q2 = x^N/N!

        mpf_clear(Q1);
        mpf_clear(Q2);

}

void E2F2F(mpf_t result, long double x, unsigned int N)
{
        mpf_t Q1,Q2,Q3;         ///gives Nth term in series expansion of 2F2

        mpf_init_set_d(Q1,(N+x)*(N+x));
        mpf_init_set_d(Q2,-x);
        mpf_init_set_d(Q3,0.0);
        mpf_init_set_d(result,0.0);

        mpf_pow_ui(Q2,Q2,N+2);    /// Q2=Q2^(N+2)=(-x)^(N+2)
        invfak(Q3,N);             /// Q3=1/N!
        mpf_mul(Q2,Q2,Q3);        /// Q2=Q2*Q3

        mpf_div(result,Q2,Q1); ///result= Q2/Q1 = .../(N+x)^2

        mpf_clear(Q1);    mpf_clear(Q3);    mpf_clear(Q2)
    }

void Exp2F2gmp(mpf_t result, long double x, unsigned int N)
{
       mpf_t Q1,Qexp,Q2F2,Qsum;
       mpf_init_set_d(Q1,0.0);
       mpf_init_set_d(Qexp,0.0);
       mpf_init_set_d(Q2F2,0.0);
       mpf_init_set_d(Qsum,0.0);

       mpf_init_set_d(result,0.0);

       for(unsigned i = 0; i <= N; ++i){
               mpf_set_d(Qsum,0.0);
               mpf_set_d(Qexp,0.0);
               mpf_set_d(Q2F2,0.0);

               for(unsigned l = 0; l <= i; ++l){ /// a_l und b_i-l
                   E2F2E(Qexp,x,l);
                   E2F2F(Q2F2,x,i-l);
                   mpf_mul(Q1,Qexp,Q2F2);
                   mpf_add(Qsum,Qsum,Q1);
               }
               mpf_add(result,result,Qsum);
               mpf_abs(Qsum,Qsum);
              //if(mpf_cmp_d(Qsum,0.00000001)==-1){ cout << "reached precision at i="<<i; break;}
       }
       cout <<  "\n\n Result = " << result << endl; 
       mpf_clear(Q1);
       mpf_clear(Qexp);
       mpf_clear(Q2F2);
       mpf_clear(Qsum);

}

f(x)函数应约为f(x)=1.05x+1f(x)>0 x>0

但是实现给出了这个:

  

Exp2F2gmp(Q,10,1000)= 12.3707

     

Exp2F2gmp(Q,20,1000)= 23.1739

     

Exp2F2gmp(Q,30,1000)= - 35195.1

     

Exp2F2gmp(Q,40,1000)= -2.92079e + 13

前两个值与Wolframalpha一致,后两个显然不同。

任何形式的帮助将不胜感激,谢谢!

1 个答案:

答案 0 :(得分:6)

这是灾难性取消的教科书范例。

2F2系列中的项增长到约exp(x)的最大大小,但2F2函数的大小约为exp(-x)。这意味着您需要至少使用log_2(exp(2x))〜= 2.886 * x额外的精度位来精确计算2F2系列,并且可能稍微更多,具体取决于计算术语的方式。

因此,例如,如果x = 29,则需要大约83位的精度。您的代码使用MPF类型的默认精度,我认为它类似于64位。您需要更改代码以将所有MPF变量的精度设置为64 + 2.886 * x位以获得64个准确位(有关如何执行此操作,请参阅GMP手册)。

在实践中,您实施的系列评估效率不高,x = 1e6可能太慢。

一种可能性是使用Arb库(我开发的)。这支持开箱即用地计算广义超几何函数,并使用更有效的系列评估策略(在这种情况下,使用二进制分割)。它还使用区间运算,因此您可以免费获得误差范围,并且可以自动设置精度而不是提前预测所需的精度(但在这种情况下,预测精度很容易,而且更快)。

以下是演示如何使用它的代码:

#include "acb_hypgeom.h"

int main()
{
    acb_t z, t;
    acb_struct a[2];
    acb_struct b[2];
    double x;

    acb_init(z); acb_init(t);
    acb_init(a + 0); acb_init(a + 1);
    acb_init(b + 0); acb_init(b + 1);

    for (x = 10.0; x <= 1000000; x *= 10)
    {
        acb_set_d(a + 0, x);
        acb_set_d(a + 1, x);
        acb_set_d(b + 0, x + 1);
        acb_set_d(b + 1, x + 1);
        acb_set_d(z, -x);
        acb_hypgeom_pfq(t, a, 2, b, 2, z, 0, 64 + 2.886 * x);
        acb_neg(z, z);
        acb_exp(z, z, 64);
        acb_mul(t, t, z, 64);
        printf("f(%f) = ", x); acb_printn(t, 20, 0); printf("\n");
    }

    acb_clear(z); acb_clear(t);
    acb_clear(a + 0); acb_clear(a + 1);
    acb_clear(b + 0); acb_clear(b + 1);
}

这是输出:

f(10.000000) = [12.37067931727649929 +/- 5.38e-18]
f(100.000000) = [106.6161729468899444 +/- 4.93e-17]
f(1000.000000) = [1020.154983574938368 +/- 3.54e-16]
f(10000.000000) = [10063.00061277849954 +/- 2.57e-15]
f(100000.000000) = [100198.5001942224819 +/- 6.28e-14]
f(1000000.000000) = [1000626.990558714621 +/- 4.59e-13]

在x = 1e6时,由于使用了290万比特,评估大约需要20秒(您的代码将花费更长时间,更长时间)。如果这仍然太慢,你需要找到一个更好的公式来计算f(x),理想情况下,当x - >时,渐近展开有效。无穷大,或者也许是一个整体表示(如果有一个没有取消问题)。

现在,如果您的2F2函数仅依赖于最终参数中的x并且修改了前四个参数,那么这个渐近展开会有一个标准公式,但随着参数的增长,我并不完全确定怎么做。由于上限和下限几乎&#34;取消&#34;,它可能会将它们视为常量并使用关于参数的标准渐近系列,但我没有检查这一点。对渐近分析有更多专业知识的人必须发表评论。

你也可以使用连续关系将2F2函数减少到参数较小的东西,但我不确定这是否会在实践中得到改善。