Thiele's interpolation formula: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|D}}: sorry, forgot reset Step to 0.05)
(→‎{{header|C}}: Currying, array slicing and exception handling removed, and uses some GCC extensions.)
Line 77: Line 77:
pi estimate using tan interpolation: 3.1415926535898
pi estimate using tan interpolation: 3.1415926535898
</pre>
</pre>
=={{header|C}}==
{{trans|ALGOL 68}} - Currying, array slicing and exception handling removed, and uses some GCC extensions.
<lang c>#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/* The MODE of lx and ly here should really be a UNION of "something double",
"something COMPLex", and "something SYMBOLIC" ... */
double thiele(int upb_lx, double lx[], double ly[], double x)
{
int upb_xx=upb_lx+1;
double *xx=lx-1, *yy=ly-1; /* shift base index to 1 */
int n=upb_xx;
/* Assuming that the values of xx are distinct ... */
double p [/*0:*/n-1 +1][/*1:*/n +1];
int i, j;
for(i=1; i<=upb_xx; i++) p[0][i]=yy[i];
for(i=1; i<=n-1; i++)p[1][i]=(xx[i]-xx[1+i])/(p[0][i]-p[0][1+i]);
for(i=2; i<=n-1; i++){
for(j=1; j<=n-i; j++){
p[i][j]=(xx[j]-xx[j+i])/(p[i-1][j]-p[i-1][j+1])+p[i-2][j+1];
}
}
double a=0;
for(i=n-1; i>=2; i--){ a=(x-xx[i])/(p[i][1]-p[i-2][1]+a); }
return yy[1]+(x-xx[1])/(p[1][1]+a);
}
main(){

double lwb_x=0, upb_x=1.55, delta_x = 0.05;
int upb_trig_table = ((upb_x-lwb_x)/delta_x);
typedef double trig_table_t [/*0:*/upb_trig_table +1];
trig_table_t x_OF_trig_table, sin_x_OF_trig_table, cos_x_OF_trig_table, tan_x_OF_trig_table;
void init_trig_table(){
int i;
for(i = 0; i<= upb_trig_table; i++){
double x = i*delta_x;
x_OF_trig_table[i]=x;
sin_x_OF_trig_table[i]=sin(x);
cos_x_OF_trig_table[i]=cos(x);
tan_x_OF_trig_table[i]=tan(x);
}
}
init_trig_table();
int upb_row = 4;
double inv_sin(double x){ return thiele(upb_trig_table, sin_x_OF_trig_table, x_OF_trig_table, x);}
double inv_cos(double x){ return thiele(upb_trig_table, cos_x_OF_trig_table, x_OF_trig_table, x);}
double inv_tan(double x){ return thiele(upb_trig_table, tan_x_OF_trig_table, x_OF_trig_table, x);}
char *result_fmt = "pi estimate using %s interpolation: %0.13f\n";
printf(result_fmt, "sin", 6*inv_sin(1.0/2));
printf(result_fmt, "cos", 3*inv_cos(1.0/2));
printf(result_fmt, "tan", 4*inv_tan(1));
}</lang>
Output:
<pre>
pi estimate using sin interpolation: 3.1415926535898
pi estimate using cos interpolation: 3.1415926535898
pi estimate using tan interpolation: 3.1415926535898
</pre>

=={{header|D}}==
=={{header|D}}==



Revision as of 11:37, 3 October 2010

Task
Thiele's interpolation formula
You are encouraged to solve this task according to the task description, using any language you may know.
This page uses content from Wikipedia. The original article was at Thiele's interpolation formula. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)

In mathematics, Thiele's interpolation formula is an interpolation formula for a function f(•) of a single variable, due to the Danish mathematician Thorvald Nicolai Thiele. It is expressed as a continued fraction, where ρ represents the reciprocal difference:

Demonstrate Thiele's interpolation function by:

  1. Building a 32 row trig table of values of the trig functions sin, cos and tan. e.g. for x from 0 by 0.05 to 1.55...
  2. Using columns from this table define an inverse - using Thiele's interpolation - for each trig function;
  3. Finally: demonstrate the following well known trigonometric identities:
    • 6 × sin-1 ½ = π
    • 3 × cos-1 ½ = π
    • 4 × tan-1 1 = π

ALGOL 68

Works with: ALGOL 68 version Revision 1 - except the Currying (aka partial parametrisation) in test block is a proposal for ALGOL 68 Rev2
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny - Currying is supported.

<lang algol68>PROC raise exception = ([]STRING msg)VOID: ( putf(stand error,("Exception:", $" "g$, msg, $l$)); stop );

  1. The MODE of lx and ly here should really be a UNION of "something REAL",

"something COMPLex", and "something SYMBOLIC" ... #

PROC thiele=([]REAL lx,ly, REAL x) REAL: BEGIN

 []REAL xx=lx[@1],yy=ly[@1];
 INT n=UPB xx;
 IF UPB yy=n THEN
  1. Assuming that the values of xx are distinct ... #
   [0:n-1,1:n]REAL p;
   p[0,]:=yy[];
   FOR i TO n-1 DO p[1,i]:=(xx[i]-xx[1+i])/(p[0,i]-p[0,1+i]) OD;
   FOR i FROM 2 TO n-1 DO
     FOR j TO n-i DO
       p[i,j]:=(xx[j]-xx[j+i])/(p[i-1,j]-p[i-1,j+1])+p[i-2,j+1]
     OD
   OD;
   REAL a:=0;
   FOR i FROM n-1 BY -1 TO 2 DO a:=(x-xx[i])/(p[i,1]-p[i-2,1]+a) OD;
   yy[1]+(x-xx[1])/(p[1,1]+a)
 ELSE
   raise exception(("Unequal length arrays supplied: ",whole(UPB xx,0)," NE ",whole(UPB yy,0))); SKIP
 FI

END;

test:(

 FORMAT real fmt = $g(0,real width-2)$;
 REAL lwb x=0, upb x=1.55, delta x = 0.05; 
 [0:ENTIER ((upb x-lwb x)/delta x)]STRUCT(REAL x, sin x, cos x, tan x) trig table;
 PROC init trig table = VOID: 
   FOR i FROM LWB trig table TO UPB trig table DO 
     REAL x = lwb x+i*delta x; 
     trig table[i]:=(x, sin(x), cos(x), tan(x))
   OD;
 init trig table;
  1. Curry the thiele function to create matching inverse trigonometric functions #
 PROC (REAL)REAL inv sin = thiele(sin x OF trig table, x OF trig table,),
                 inv cos = thiele(cos x OF trig table, x OF trig table,),
                 inv tan = thiele(tan x OF trig table, x OF trig table,);
 printf(($"pi estimate using "g" interpolation: "f(real fmt)l$, 
   "sin", 6*inv sin(1/2),
   "cos", 3*inv cos(1/2),
   "tan", 4*inv tan(1)
 ))

)</lang> Output:

pi estimate using sin interpolation: 3.1415926535898
pi estimate using cos interpolation: 3.1415926535898
pi estimate using tan interpolation: 3.1415926535898

C

Translation of: ALGOL 68

- Currying, array slicing and exception handling removed, and uses some GCC extensions.

<lang c>#include <stdlib.h>

  1. include <stdio.h>
  2. include <math.h>

/* The MODE of lx and ly here should really be a UNION of "something double", "something COMPLex", and "something SYMBOLIC" ... */

double thiele(int upb_lx, double lx[], double ly[], double x) {

 int upb_xx=upb_lx+1;
 double *xx=lx-1, *yy=ly-1; /* shift base index to 1 */
 int n=upb_xx;

/* Assuming that the values of xx are distinct ... */

 double p [/*0:*/n-1 +1][/*1:*/n +1];
 int i, j;
 for(i=1; i<=upb_xx; i++) p[0][i]=yy[i];
 for(i=1; i<=n-1; i++)p[1][i]=(xx[i]-xx[1+i])/(p[0][i]-p[0][1+i]);
 for(i=2; i<=n-1; i++){
   for(j=1; j<=n-i; j++){
     p[i][j]=(xx[j]-xx[j+i])/(p[i-1][j]-p[i-1][j+1])+p[i-2][j+1];
   }
 }
 double a=0;
 for(i=n-1; i>=2; i--){ a=(x-xx[i])/(p[i][1]-p[i-2][1]+a); }
 return yy[1]+(x-xx[1])/(p[1][1]+a);

}

main(){

 double lwb_x=0, upb_x=1.55, delta_x = 0.05; 

 int upb_trig_table = ((upb_x-lwb_x)/delta_x);
 typedef double trig_table_t [/*0:*/upb_trig_table +1];
 trig_table_t x_OF_trig_table, sin_x_OF_trig_table, cos_x_OF_trig_table, tan_x_OF_trig_table;

 void init_trig_table(){
   int i;
   for(i = 0; i<= upb_trig_table; i++){
     double x = i*delta_x; 
     x_OF_trig_table[i]=x;
     sin_x_OF_trig_table[i]=sin(x);
     cos_x_OF_trig_table[i]=cos(x);
     tan_x_OF_trig_table[i]=tan(x);
   }
 }

 init_trig_table();

 int upb_row = 4;
 double inv_sin(double x){ return thiele(upb_trig_table, sin_x_OF_trig_table, x_OF_trig_table, x);}
 double inv_cos(double x){ return thiele(upb_trig_table, cos_x_OF_trig_table, x_OF_trig_table, x);}
 double inv_tan(double x){ return thiele(upb_trig_table, tan_x_OF_trig_table, x_OF_trig_table, x);}

 char *result_fmt = "pi estimate using %s interpolation: %0.13f\n";
 printf(result_fmt, "sin", 6*inv_sin(1.0/2));
 printf(result_fmt, "cos", 3*inv_cos(1.0/2));
 printf(result_fmt, "tan", 4*inv_tan(1));

}</lang> Output:

pi estimate using sin interpolation: 3.1415926535898
pi estimate using cos interpolation: 3.1415926535898
pi estimate using tan interpolation: 3.1415926535898

D

<lang d>import std.stdio ; import std.math ;

U[] myMap(U,V) (U delegate(V) f, V[] a) {

   V[] r = new V[](a.length) ;
   foreach(i, v ; a)
       r[i] = f(v) ;
   return r ;    

}

T[] myRng(T)(T start, T end, T step) {

   T[] r ;
   for(; start < end ; start += step)
       r ~= start ;
   return r ;

}

alias real delegate(real) RealFun ; const real End = 1.55L , Step = 0.05L , Start = 0.0L ;

class Thiele {

   private real[] F, X, RhoY, RhoX ;
   const int NN ;
   
   this(real[] f, real[] x) {
       F   = f.dup ;
       X   = x.dup ;
       NN  = X.length ;
       assert(NN > 2, "at leat 3 values") ;
       assert(X.length == F.length, "input arrays not of same size") ;
       RhoY = rhoN(F, X) ;
       RhoX = rhoN(X, F) ;
   }
   this(RealFun fun, real start = Start, real end = End, real step = Step) {
       this(myMap(fun, myRng(start, end, step)), 
            myRng(start, end, step)) ;
   }
   private static real[] rhoN(real[] f, real[] x) {
       int N = x.length;
       real[][] p = new real[][] (N, N) ;
       for(int i = 0; i < N ; i++)
           p[i][0] = f[i] ;
       for(int i = 0; i < N - 1 ; i++)
           p[i][1] = (x[i] - x[i+1]) / (p[i][0] - p[i+1][0]) ;
       for(int j = 2; j < N - 1; j++)
           for(int i = 0; i < N - j - 1 ; i++)
               p[i][j] = p[i+1][j-2] + (x[i] - x[i+j]) /  
                         (p[i][j-1] - p[i+1][j-1]) ;                           ;
       return p[1].dup ;    
   }
   real evalY(real x) {
       real a = 0.0L ;
       for(int i = NN - 3; i >= 2 ; i--)
           a = (x - X[i]) / (RhoY[i] - RhoY[i-2] + a) ;
       return F[1] + (x - X[1]) / (RhoY[1] + a) ;        
   }
   real evalX(real y) { // inverse
       real a = 0.0L ;
       for(int i = NN - 3; i >= 2 ; i--)
           a = (y - F[i]) / (RhoX[i] - RhoX[i-2] + a) ;
       return X[1] + (y - F[1]) / (RhoX[1] + a) ;        
   }

}

void main() {

   auto fun = (real x) { return std.math.sin(x) ; } ;
   auto t = new Thiele(fun) ;
   writefln(" %d interpolating points", t.NN) ;
   writefln("std.math.sin(0.5) : %20.18f", std.math.sin(0.5L)) ;
   writefln("  Thiele sin(0.5) : %20.18f", t.evalY(0.5L)) ;    
   writefln("*%20.18f library constant", std.math.PI) ;
   writefln(" %20.18f 6*sin`(0.5)", t.evalX(0.5L) * 6.0L) ;
   t = new Thiele((real x) {return std.math.cos(x) ; }) ;
   writefln(" %20.18f 3*cos`(0.5)", t.evalX(0.5L) * 3.0L) ;
   t = new Thiele((real x) {return std.math.tan(x) ; }) ;
   writefln(" %20.18f 4*tan`(0.5)", t.evalX(1.0L) * 4.0L) ;

}</lang>

output:

 32 interpolating points
std.math.sin(0.5) : 0.479425538604203000
  Thiele sin(0.5) : 0.479425538604203000
*3.141592653589793239 library constant
 3.141592653589793238 6*sin`(0.5)
 3.141592653589793238 3*cos`(0.5)
 3.141592653589793238 4*tan`(0.5)

Tcl

This example is incorrect. Please fix the code and remove this message.

Details: It produces the wrong values for output.

Works with: Tcl version 8.5

<lang tcl># Multi-list map; deeper voodoo than normal! proc map args {

   foreach {a b} [lrange $args 0 end-1] {

lappend parms [incr v] $b upvar 1 $a $v

   }
   foreach {*}$parms {lappend result [uplevel 1 [lindex $args end]]}
   return $result

}

proc thiele {X Y x} {

   # Sanity check
   if {[llength $X] != [llength $Y]} {

error "unequal length lists supplied: [llength $X] != [llength $Y]"

   }
   #
   ### Compute the table of reciprocal differences
   #
   set n [expr {[llength $Y] - 1}]
   # Zero-th order
   set r [list $Y]
   # First order
   lappend r [map a [lrange $X 0 end-1] b [lrange $X 1 end] \

c [lrange $Y 0 end-1] d [lrange $Y 1 end] \ e [lrepeat $n 0] { expr {($a - $b) / ($c - $d + $e)}

   }]
   # Higher order
   for {set i 2} {$i <= $n} {incr i} {

set p [lindex $r end]; # previous row set pp [lindex $r end-1]; # doubly-previous row; longer than $p by 1 lappend r [map a [lrange $X 0 end-$i] b [lrange $X $i end] \ c [lrange $p 0 end-1] d [lrange $p 1 end] \ e [lrange $pp 1 end-1] { expr {($a - $b) / ($c - $d + $e)} }]

   }
   #
   ### Actually evaluate Thiele's formula
   #
   set a 0.0
   foreach b [lreverse [lrange $X 2 end]] c [lreverse [lrange $r 2 end]] \

d [lreverse [lrange $r 0 end-2]] { set a [expr {($x - $b) / ([lindex $c 0] - [lindex $d 0] + $a)}]

   }
   expr {[lindex $Y 0] + ($x - [lindex $X 0]) / ([lindex $r 1 0] + $a)}

}</lang> Demonstration code: <lang tcl>proc initThieleTest {} {

   for {set i 0} {$i < 32} {incr i} {

lappend trigTable(x) [set x [expr {0.05 * $i}]] lappend trigTable(sin) [expr {sin($x)}] lappend trigTable(cos) [expr {cos($x)}] lappend trigTable(tan) [expr {tan($x)}]

   }
   interp alias {} invSin {} thiele $trigTable(sin) $trigTable(x)
   interp alias {} invCos {} thiele $trigTable(cos) $trigTable(x)
   interp alias {} invTan {} thiele $trigTable(tan) $trigTable(x)

} initThieleTest puts "pi estimate using sin interpolation: [expr {6 * [invSin 0.5]}]" puts "pi estimate using cos interpolation: [expr {3 * [invCos 0.5]}]" puts "pi estimate using tan interpolation: [expr {4 * [invTan 1.0]}]"</lang> Output:

pi estimate using sin interpolation: 3.7607373341712798
pi estimate using cos interpolation: 1.3300617930864258
pi estimate using tan interpolation: 8.98541899822322