Числовой интеграл от 0 до бесконечности

Моя цель — рассчитать численный интеграл функции распределения вероятностей (PDF) расстояния электрона от ядра атома водорода на языке программирования C. Я написал пример кода, однако он не может правильно найти числовое значение из-за того, что я не могу увеличить предел настолько, насколько это необходимо, по моему мнению. Я также включил библиотеку, но не могу использовать значения, указанные в следующем сообщении, в качестве интегральных границ: минимальное и максимальное значение типа данных в C . Какое средство в этом случае? Может стоит перейти на другой язык программирования? Любая помощь и предложение приветствуются, спасибо заранее.

Изменить: после некоторого значения я получаю ошибку сегментации ошибки. Я проверил, что фактический результат интеграла равен 0,0372193 с помощью Wolframalpha. В дополнение к этому, если я увеличиваю k на меньшие суммы, я получаю в результате ноль, поэтому я определил r[k]=k, я знаю, что он должен быть меньше для повышения точности.

#include <stdio.h>
#include <math.h>
#include <limits.h>
#define a0 0.53 
int N = 200000;
// This value of N is the highest possible number in long double
// data format. Change its value  to adjust the precision of integration
// and computation time.
// The discrete integral may be defined as follows:
long double trapezoid(long double x[], long double f[]) {
  int i;
  long double dx = x[1]-x[0];
  long double sum = 0.5*(f[0]+f[N]);

    for (i = 1; i <  N; i++) 
        sum+=f[i];
  return sum*dx;  
}
main() {

    long double P[N], r[N], a;
    // Declare and initialize the loop variable
    int k = 0;
    for (k = 0; k <  N; k++)
    {
        r[k] = k ;
        P[k] = r[k] * r[k] * exp( -2*r[k] / a0);
        //printf("%.20Lf \n", r[k]);
        //printf("%.20Lf \n", P[k]);
    }
    a = trapezoid(r, P);    
    printf("%.20Lf \n", a);
}

Последний код:

#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <stdlib.h>
#define a0 0.53 
#define N LLONG_MAX
// This value of N is the highest possible number in long double
// data format. Change its value  to adjust the precision of integration
// and computation time.
// The discrete integral may be defined as follows:
long double trapezoid(long double x[],long double f[]) {
  int i;
  long double dx = x[1]-x[0];
  long double sum = 0.5*(f[0]+f[N]);

    for (i = 1; i <  N; i++) 
        sum+=f[i];
  return sum*dx;  
}
main() {
    printf("%Ld", LLONG_MAX);
    long double * P = malloc(N * sizeof(long double));
    long double * r = malloc(N * sizeof(long double));
    // Declare and initialize the loop variable
    int k = 0;
    long double integral;
    for (k = 1; k <  N; k++)
        {
        P[k] = r[k] * r[k] * expl( -2*r[k] / a0);
        }
    integral = trapezoid(r, P);
    printf("%Lf", integral);

}

Отредактируйте последний рабочий код:

#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <stdlib.h>
#define a0 0.53 
#define N LONG_MAX/100
// This value of N is the highest possible number in long double
// data format. Change its value  to adjust the precision of integration
// and computation time.
// The discrete integral may be defined as follows:
long double trapezoid(long double x[],long double f[]) {
  int i;
  long double dx = x[1]-x[0];
  long double sum = 0.5*(f[0]+f[N]);

    for (i = 1; i <  N; i++) 
        sum+=f[i];
  return sum*dx;  
}
main() {
    printf("%Ld \n", LLONG_MAX);
    long double * P = malloc(N * sizeof(long double));
    long double * r = malloc(N * sizeof(long double));
    // Declare and initialize the loop variable
    int k = 0;
    long double integral;
    for (k = 1; k <  N; k++)
        {
        r[k] = k / 100000.0;
        P[k] = r[k] * r[k] * expl( -2*r[k] / a0);
        }
    integral = trapezoid(r, P);
    printf("%.15Lf \n", integral);
    free((void *)P);
    free((void *)r);
}

В частности, я изменил определение для r[k], используя число с плавающей запятой в операции деления, чтобы в результате получить длинное двойное значение, а также, как я заявил в своем последнем комментарии, я не могу использовать Ns, превышающие LONG_MAX/100. и я думаю, что мне следует дополнительно изучить код и malloc, чтобы решить проблему. Я нашел точное значение, полученное аналитически путем взятия пределов; Я подтвердил результат с TI-89 Titanium и Wolframalpha (как численно, так и аналитически), помимо того, что сделал это сам. Правило трапеций сработало довольно хорошо, когда размер интервала был уменьшен. Большое спасибо за все плакаты здесь за их идеи. Между прочим, значение 2147483647 LONG_MAX не так уж велико, как я ожидал, не должно ли ограничение быть около десяти в степени 308?


person Vesnog    schedule 01.11.2013    source источник
comment
Взгляните на библиотеку GMP (также известную как библиотека BigNum...)   -  person Kninnug    schedule 01.11.2013
comment
Я не могу использовать значения, указанные в следующем посте... что заставляет вас так говорить? вы получаете какие-либо ошибки?   -  person A4L    schedule 01.11.2013
comment
@Kninnug Это кажется разумным, легко ли реализовать эту библиотеку. Я спрашиваю об этом, потому что я никогда не делал этого раньше.   -  person Vesnog    schedule 01.11.2013
comment
@A4LК сожалению, в данный момент мой терминал не на английском языке, но я получаю какую-то ошибку переполнения.   -  person Vesnog    schedule 01.11.2013
comment
@Vesnog динамически выделяет память, как показано Зетами.   -  person A4L    schedule 01.11.2013
comment
@ A4L сделал это, но я все еще получаю дамп ядра ошибки сегментации, когда пытаюсь распечатать вывод. Кстати, код компилируется нормально. Я включу последнюю версию в свое редактирование.   -  person Vesnog    schedule 01.11.2013
comment
Подынтегральная функция экспоненциально убывает, так что правило трапеций с разумной отсечкой вполне подойдет, по крайней мере, в качестве первого прохода для решения задачи. Массивы слишком короткие на единицу - они должны быть длиной N+1. Есть разница в обычном математическом представлении правила трапеций (f[0] и f[N] — значения на концах) и массивов C, для которых f[0] — первое значение, а f[N -1] — последнее значение. Эта разница также проявляется в цикле C — индекс цикла достигает только N-1, поэтому значение f[N] не определено.   -  person Mark Dewing    schedule 27.11.2017


Ответы (1)


Численная точка зрения

Обычный метод трапеций не работает с несобственными интегралами. Таким образом, квадратурные правила Гаусса намного лучше, поскольку они не только обеспечивают точность 2n-1 (то есть для многочлена степени 2n-1 они вернут правильное решение), но также управляют неправильными интегралами, используя правильную весовую функцию. .

Если ваш интеграл неверен с обеих сторон, вам следует попробовать квадратуру Гаусса-Эрмита, иначе используйте квадратуру Гаусса-Лагерра.

Ошибка "переполнение"

long double P[N], r[N], a;

P имеет размер примерно 3 МБ, как и r. Это слишком много памяти. Вместо этого выделите память:

long double * P = malloc(N * sizeof(long double));
long double * r = malloc(N * sizeof(long double));

Не забудьте включить <stdlib.h> и использовать free как для P, так и для r, если они вам больше не нужны. Кроме того, вы не можете получить доступ к N-й записи, поэтому f[N] неверно.

Используя квадратуру Гаусса-Лагерра

Теперь Гаусс-Лагер использует exp(-x) в качестве весовой функции. Если вы не знакомы с квадратурой Гаусса: результат E(f) — это интеграл от w * f, где w — весовая функция.

Ваш f выглядит так, и:

f x = x^2 * exp (-2 * x / a)

Подождите минуту. f уже содержит exp(-term), поэтому мы можем заменить x на t = x * a /2 и получить

f' x = (t * a/2)^2 * exp(-t) * a/2

Поскольку exp(-t) уже является частью нашей функции веса, ваша функция теперь идеально вписывается в квадратуру Гаусса-Лагерра. Полученный код

#include <stdio.h>
#include <math.h>

/* x[] and a[] taken from 
 * https://de.wikipedia.org/wiki/Gau%C3%9F-Quadratur#Gau.C3.9F-Laguerre-Integration
 * Calculating them by hand is a little bit cumbersome
*/
const int gauss_rule_length = 3;
const double gauss_x[] = {0.415774556783, 2.29428036028, 6.28994508294};
const double gauss_a[] = {0.711093009929, 0.278517733569, 0.0103892565016};

double f(double x){
    return x *.53/2 * x *.53/2 * .53/2;
}

int main(){
    int i;
    double sum = 0;
    for(i = 0; i < gauss_rule_length; ++i){
        sum += gauss_a[i] * f(gauss_x[i]);
    }
    printf("%.10lf\n",sum); /* 0.0372192500 */
    return 0;
}
person Zeta    schedule 01.11.2013
comment
Спасибо за Ваш ответ. Однако я довольно неопытен в других квадратурах, и наш инструктор сказал нам, что мы будем использовать правило трапеций с небольшими интервалами. - person Vesnog; 01.11.2013
comment
@Vesnog: Ой. Правило трапеций — дешевый метод для замкнутых интервалов/кубоидов (определенных интегралов). - person Zeta; 01.11.2013
comment
Я просто выполняю его инструкции и не имею опыта в других методах. - person Vesnog; 01.11.2013
comment
Правило трапеции глупо для этой задачи. Скажи это своему инструктору. - person David Heffernan; 01.11.2013
comment
Спасибо за ваши усилия, я сделал, как вы сказали. Тем не менее, я все еще получаю ошибку сегментации (сброс ядра) в случае трапеции. Для квадратуры Гаусса, как мы можем рассчитать необходимые параметры? Я спрашиваю об этом, потому что вы сказали, что это немного утомительно, и я хочу полностью понять метод, если я должен его использовать. - person Vesnog; 01.11.2013
comment
@Vesnog: раздел Wiki: вы берете весовую функцию, создаете последовательность ортогональных полиномов на основе его индуцированного скалярного произведения, а затем вычислить нули n-го многочлена (для квадратуры n-го порядка). Вы можете рассчитать веса по формуле (также приведенной в статье). Однако статья не так хороша, как хорошо написанная книга. Полное описание квадратуры Гаусса было бы слишком много для комментария или вопроса, извините :/. - person Zeta; 01.11.2013
comment
@Zeta Хорошо, спасибо, я буду изучать квадратуру Гаусса позже в своем классе, я полагаю, но теперь я хочу продолжить с правилом трапеций, но я получаю ошибку сегментации, когда запускаю скомпилированный код. - person Vesnog; 01.11.2013
comment
@Vesnog: f[N] недопустимо. Кроме того, имейте в виду, что на вашем компьютере, вероятно, не хватит памяти для такого количества значений. Вы должны проверить возврат malloc. Если вы еще не знаете malloc, сейчас самое время почитать книгу по языку программирования C ;). - person Zeta; 01.11.2013
comment
Благодаря ошибке сегментации появляется ошибка, когда для параметра N установлено значение больше, чем LONG_MAX/100 из пакета лимитов. Я включил это только для информации. Думаю, я проверю функцию malloc, чтобы лучше понять динамику, стоящую за этим. Кстати, для этого значения N функция дает именно то значение, которое получается аналитически при решении интеграла самостоятельно и на Вольфрамальфе (как численно, так и аналитически). 0,03721925, если быть точным. - person Vesnog; 02.11.2013
comment
Я не согласен, что правило трапеций плохо по своей сути. В любом случае сходящийся интеграл будет близок к нулю для больших значений x, поэтому отключение интегрирования где-то ближе к концу во многих случаях даст вам достаточную точность. Это можно проверить и по критериям сходимости. - person rubenvb; 02.11.2013