[ccan] Mathematics Module

Rusty Russell rusty at rustcorp.com.au
Wed Mar 18 15:22:13 EST 2009


On Friday 06 February 2009 13:56:53 Rupinder Singh wrote:
> Hello,
> 
>   I think your original code was in fact optimal.  For example, it takes
> > about 7.1 seconds to figure out that 36028797018963971 is prime on my
> > 64 bit machine here.
> 
> The original routine (is_prime) is still there. I had expressed in my
> previous mail
> > but is_prime should be used for that due to its superior efficiency.
> 
> It takes less than a second to determine the primality of 36028797018963971
> by is_prime. is_prime2 is the implementation of Sieve of Eratosthenes to
> determine the prime numbers, not very efficient to determine primality, as
> you just said.

Yes, but it's not useful.  I've dropped it.

> Here are a few additions :
> 
> 1) nCr - A Memory and Time efficient routine to calculate n! / (n-r)! r! .

This is OK.

> 2) newton_raphson - Determination of root, Newton-Raphson method. The user
> has to overload the function fn(x) & fnd(x)  [its derivative] to implement
> his given function.
>
> 3) ivp - Solution to the Initial Value Problem dy/dx = fnd2(x,y) , Given
> y(x0) = y0, y(x1) = ?
> 
> 4) romberg_integral - Definite Integration within closed limits [a, b],
> Romberg's Method. fn(x) has to be overloaded.

These need to take the function(s) as arguments, rather than using globals.

Here's what I currently have:

===
#ifndef CCAN_NUMERIC_H
#define CCAN_NUMERIC_H

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

/**
 * is_square - Is the number a perfect square?
 * @n: number to test
 *
 * Example:
 *	if (is_square(4))
 *		printf("%d is indeed a Perfect Square!", 4);
 */
static inline bool is_square(unsigned long n)
{
	unsigned long tmp = sqrt((double)n);

	return tmp*tmp == n;
}

/**
 * is_prime - Is the number prime ?
 * @n: number to test
 *
 * Example:
 *	if (is_prime(17))
 *		printf("%d is Prime!\n", 17);
 */
static inline bool is_prime(unsigned long n)
{
	unsigned long i;
	unsigned int skip3 = 1;

	if(n == 2 || n == 3)
		return true;

	if(n%2 == 0 || n%3 == 0)
		return false;

	for(i=5; i*i <= n; i+=(2 << (skip3 ^= 1)))
		if(n%i == 0)
			return false;

	return true;
}

/**
 * gcd - Returns the GCD of two numbers
 * @a: first number
 * @b: second number
 * Example:
 *	if (gcd(5,7)==1)
 *		printf("gcd of 5 & 7 is 1");
 */
static inline int gcd(long a,long b)
{
    if(b==0) return (a);
    return (gcd(b,a%b));
}

/**
 * nCr - Calculates nCr efficiently, n! / (n-r)!r!
 *
 * Example:
 *	printf("%lu", nCr(6, 2));
 *
 *  15
 */
unsigned long nCr(unsigned long n, unsigned long r);

/**
 * newton_raphson - Returns the root of the function f(x) by the method of 
 * @fn: function to test
 * @fnd: derivative of the function
 * @seed: seed point
 * @eps: x is considered a root if |fn(x)| < eps
 * @delta: 
 * @iter: number of iterations to perform
 * Example:
 *	printf("%lf", newton_raphson(2, 0.001, 0.01, 1000);
 *
 *  3
 */
double newton_raphson(double (*fn)(double x),
		      double (*fnd)(double x),
		      double seed, double eps, double delta,
		      unsigned int iter);

/**
 * ivp - Solves the Initial Value Problem of the type dy/dx = f(x,y), y(x0) = y0, y(x1) = ?
 * @fnd2: dy/dx
 * 
 * In the example : y = e^x^2 ; dy/dx = xy
 *
 * Example:
 *	printf("%lf", ivp(0, 1, 1, 1000);
 *
 *  1.6487
 */
double ivp(double (*fnd2)(double x, double y),
	   double x0, double y0, double x1, unsigned int iter);

/**
 * romberg_integral - Calculates definite integral in the closed interval [a,b]
 *
 * @a : lower limit of the interval
 * @b : upper limit of the interval (up to ROMBERG_MAXITER)
 * Example:
 *	printf("%lf", romberg_integral(1, 2));
 *
 *  163.33333
 */
double romberg_integral(double (*fn)(double x),
			double a, double b,
			unsigned int itermin, unsigned int itermax);


/**
 * ROBERG_MAXITER - the ceiling of iterations for romberg_integral()
 */
#ifndef ROMBERG_MAXITER
#define ROMBERG_MAXITER 100
#endif

#endif /* CCAN_NUMERIC_H */
===
#include "numeric.h"

static inline void div_by_gcd(unsigned long *a, unsigned long *b)
{
	unsigned long g=gcd(*a,*b);
	*a/=g;
	*b/=g;
}

unsigned long nCr(unsigned long n, unsigned long r)
{
	unsigned long numerator=1,denominator=1,toMul,toDiv,i;
	if (r > n/2) r = n-r; // use smaller r
	for (i=r ; i ; i--)
	{
		toMul = n-r+i;
		toDiv = i;
		div_by_gcd(&toMul,&toDiv); /* always divide before multiply */
		div_by_gcd(&numerator,&toDiv);
		div_by_gcd(&toMul,&denominator);
		numerator *= toMul;
		denominator *= toDiv;
	}

	return numerator/denominator;
}

double newton_raphson(double (*fn)(double x),
		      double (*fnd)(double x),
		      double seed, double eps, double delta,
		      unsigned int iter)
{
	unsigned int i;
	double x2 = 0, x1;

	if ( !fnd(seed)) {seed += eps;}

	x1 = seed - fn(seed)/fnd(seed);

	for (i=0;i<iter;i++)
	{
		if (fabs(fnd(x1)) < delta)
			return 0.0/0.0;

		else x2 = x1 - fn(x1)/fnd(x1);
		if (fabs(fn(x2)) < eps) return x2;
		else {seed=x1; x1=x2;}
	}

	return x2;
}

double ivp(double (*fnd2)(double x, double y),
	   double x0, double y0, double x1, unsigned int iter)
{
	unsigned int i;
	double yn, yp=y0;
	double h = (x1-x0)/iter;

	for (i=0;i<iter;i++)
	{
		yn = yp + h*fnd2(x0, yp);
		yp = yn;
		x0 += h;
	}

	return yp;
}

double romberg_integral(double (*fn)(double x),
			double a, double b,
			unsigned int itermin, unsigned int itermax)
{
	int i,j,n=0;
    double d,r,s,ta;
    double I[ROMBERG_MAXITER][ROMBERG_MAXITER];
    if (itermax>ROMBERG_MAXITER) itermax=ROMBERG_MAXITER;
    r = fn(a);
    ta = (r + fn(b))/2;
    d = b-a;
    I[0][0] = ta*d;
	do
	{
		n = n+1;
		d /= 2;
		s=ta;
		for (i=1; i < pow(2.0,n); i++)
			s += fn(a + d*i);

		I[0][n] = s*d;
		r=1;
		for (i=1; i<n+1; i++)
		{
			r=r*4;
			j=n-i;
			I[i][j] = (r*I[i-1][j+1] - I[i-1][j])/(r-1);
		}

		if (n >= itermax) return I[n][0];
	} while (n < itermin);

	return I[n][0];
}



More information about the ccan mailing list