GMP дает неверный результат расширения серии

Я использую пакет GMP для реализации продукта двух функций, которые я выражаю как продукт Коши двух сходящихся рядов.

Более подробно: ищу способ рассчитать f(x)=g(x)*h(x) где g(x) экспоненциальная функция и h(x) специальная гипергеометрическая функция (см. ниже), выраженная в виде ряда.

Моя проблема в том, что это работает и согласуется с моим собственным приближением и результатами Вольфрамальфы для x<29, но не для x>29, На практике мне нужны значения около x=10^6,


3 формулы, которые я использовал, изображены на рисунке ниже:

формулы


Код

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+1 а также, f(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

Решение

Это учебник пример катастрофической отмены.

Слагаемые в серии 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.

Одна из возможностей заключается в использовании Арб библиотека (которую я разрабатываю). Это поддерживает вычисление обобщенных гипергеометрических функций из коробки и использует гораздо более эффективную стратегию оценки серии (в этом случае, используя двоичное расщепление). Он также использует интервальную арифметику, поэтому вы бесплатно получаете границы ошибок и можете установить точность автоматически, вместо того, чтобы заранее прогнозировать необходимую точность (но в этом случае прогнозировать точность легко и быстрее).

Вот код, демонстрирующий, как его использовать:

#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 оценка занимает около 20 секунд (ваш код займет гораздо больше времени) в результате использования 2,9 миллиона битов. Если это все еще слишком медленно, вам нужно найти лучшую формулу для вычисления f (x), в идеале — асимптотическое расширение, допустимое, когда x -> бесконечность, или, возможно, интегральное представление (если оно существует без проблем отмены).

Теперь, если ваша функция 2F2 зависит только от x в последнем аргументе и имеет фиксированные первые четыре параметра, для этой асимптотики будет стандартная формула, но с учетом растущих параметров я не совсем уверен, как это сделать. , Так как верхний и нижний параметры почти «отменяют», это может сработать, чтобы рассматривать их как константы и использовать стандартные асимптотические ряды по аргументу, но я не проверял это. Кто-то с большим опытом в асимптотическом анализе должен был бы прокомментировать.

Также возможно, что вы могли бы использовать смежные отношения, чтобы уменьшить функцию 2F2 до чего-то с небольшими параметрами, но я не уверен, что это будет улучшение на практике.

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]