1

My aim is to calculate the numerical integral of a probability distribution function (PDF) of the distance of an electron from the nucleus of the hydrogen atom in C programming language. I have written a sample code however it fails to find the numerical value correctly due to the fact that I cannot increase the limit as much as its necessary in my opinion. I have also included the library but I cannot use the values stated in the following post as integral boundaries: min and max value of data type in C . What is the remedy in this case? Should switch to another programming language maybe? Any help and suggestion is appreciated, thanks in advance.

Edit: After some value I get the error segmentation fault. I have checked the actual result of the integral to be 0.0372193 with Wolframalpha. In addition to this if I increment k in smaller amounts I get zero as a result that is why I defined r[k]=k, I know it should be smaller for increased precision.

#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);
}

Last Code:

#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);

}

Edit last code working:

#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);
}

In particular I have changed the definition for r[k] by using a floating point number in the division operation to get a long double as a result and also as I have stated in my last comment I cannot go for Ns larger than LONG_MAX/100 and I think I should investigate the code and malloc further to get the issue. I have found the exact value that is obtained analytically by taking the limits; I have confirmed the result with TI-89 Titanium and Wolframalpha (both numerically and analytically) apart from doing it myself. The trapezoid rule worked out pretty well when the interval size has been decreased. Many thanks for all the posters here for their ideas. Having a value of 2147483647 LONG_MAX is not that particularly large as I expected by the way, should the limit not be around ten to power 308?

Community
  • 1
  • 1
Vesnog
  • 773
  • 2
  • 16
  • 33
  • Take a look at the [GMP library](http://gmplib.org/) (also known as the BigNum library...) – Kninnug Nov 01 '13 at 10:01
  • *I cannot use the values stated in the following post ...* what makes you say that? do you get any errors? – A4L Nov 01 '13 at 10:09
  • @Kninnug That seems reasonable, is it easy to implement this library. I am asking this since I have never done so before. – Vesnog Nov 01 '13 at 10:16
  • @A4LUnfortunately my terminal is not in English at the moment but I get some sort of overflow error. – Vesnog Nov 01 '13 at 10:17
  • @Vesnog allocate memory dynamically as shown by Zeta. – A4L Nov 01 '13 at 10:29
  • @A4L done so but I still get segmentation fault core dumped when I try to printf the output. Btw the code compiles fine. I will include the latest version in my edit. – Vesnog Nov 01 '13 at 12:33
  • The integrand is exponentially decreasing, so the trapezoidal rule with a reasonable cutoff will work just fine, at least as a first pass for solving the problem. The arrays are too short by one - they should be length N+1. There is a difference in the usual mathematical presentation of the trapezoid rule (f[0] and f[N] are the values at the endpoints), and C arrays, for which f[0] is the first value, and f[N-1] is the last value. This difference also shows up in the C loop - the loop index only reaches N-1, and so the value of f[N] is undefined. – Mark Dewing Nov 27 '17 at 10:39

1 Answers1

7

Numerical point of view

The usual trapezoid method doesn't work with improper integrals. As such, Gaussian quadrature rules are much better, since they not only provide 2n-1 exactness (that is, for a polynomial of degree 2n-1 they will return the correct solution), but also manage improper integrals by using the right weight function.

If your integral is improper in both sides, you should try the Gauss-Hermite quadrature, otherwise use the Gauss-Laguerre quadrature.

The "overflow" error

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

P has a size of roughly 3MB, and so does r. That's too much memory. Allocate the memory instead:

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

Don't forget to include <stdlib.h> and use free on both P and r if you don't need them any longer. Also, you may not access the N-th entry, so f[N] is wrong.

Using Gauss-Laguerre quadrature

Now Gauss-Laguerre uses exp(-x) as weight function. If you're not familiar with Gaussian quadrature: the result of E(f) is the integral of w * f, where w is the weight function.

Your f looks like this, and:

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

Wait a minute. f already contains exp(-term), so we can substitute x with t = x * a /2 and get

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

Since exp(-t) is already part of our weight function, your function fits now perfectly into the Gauss-Laguerre quadrature. The resulting code is

#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;
}
Zeta
  • 103,620
  • 13
  • 194
  • 236
  • Thanks for your answer. However I am pretty inexperience about other quadratures and our instructor told us that we shall use trapezoid rule with small intervals. – Vesnog Nov 01 '13 at 10:13
  • @Vesnog: Ouch. The trapezoid rule is a cheap method for closed intervals/cuboids (definite integrals). – Zeta Nov 01 '13 at 10:18
  • I am just complying his instructions and have no experience in other methods. – Vesnog Nov 01 '13 at 10:22
  • 4
    Trapezoid rule is stupid for this problem. Tell your instructor that. – David Heffernan Nov 01 '13 at 10:27
  • Thanks for your effort I have done as you told. However, I am still getting segmentation fault (core dumped) error in the trapezoid case. For the Gaussian quadrature how can we calculate the parameters needed? I am asking this since you told that it is a bit tedious to do so and I want to comprehend the method fully if I am to use it. – Vesnog Nov 01 '13 at 12:20
  • @Vesnog: [Wiki section](https://en.wikipedia.org/wiki/Gaussian_quadrature#Computation_of_Gaussian_quadrature_rules): you take the weight function, create a sequence of orthogonal polynomials based on its induced scalar product and then calculate the zeros of the nth-polynomial (for a nth-order quadrature). You can calculate the weights using a formula (also given in the article). However, the article is not as good as a well-written book. A complete description of Gaussian quadrature would be too much for a comment or a question, sorry :/. – Zeta Nov 01 '13 at 12:32
  • @Zeta Okay thanks I will learn Gaussian quadrature later in my class I presume but now I want to continue with the trapezoid rule, but I get segmentation fault when I run the compiled code. – Vesnog Nov 01 '13 at 12:36
  • @Vesnog: `f[N]` isn't valid. Also, keep in mind that your PC probably won't have enough memory for that many values. You should check the return of `malloc`. If you don't know `malloc` yet, this is a good time to also check a book for the C programming language ;). – Zeta Nov 01 '13 at 12:42
  • Thanks the segmentation fault error appears when the N parameter is set to a value larger than LONG_MAX/100 from the limits package. I have included this just for you information. I shall check the malloc function to better understand the dynamics behind this I guess. By the way for this value of N the function gives precisely the value obtain analytically by solving the integral myself and on Wolframalpha (both numerically and analytically). 0.03721925 to be exact. – Vesnog Nov 02 '13 at 14:25
  • I don't agree the trapezoid rule is bad inherently. A converging integral will be near zero for large values of x anyways, so cutting off the integration somewhere near the end will get you enough accuracy in many cases. This can be verified by convergence criteria as well. – rubenvb Nov 02 '13 at 14:35