Colabora .NET

Métodos numéricos en dispositivos móviles

 

Fecha: 27/Jul/2006 (25-07-06)
Autor: Manuel Sandoval -webmailusr-msn@yahoo.com

 


1. INTRODUCCIÓN

Me tocó el caso de escribir para un dispositivo móvil una pequeña aplicación que requería usar la función arcotangente, la cual no estaba disponible en el hardware, a pesar de que el seno y coseno sí estaban definidos.

La función arcotangente devuelve el valor del ángulo correspondiente al argumento. Es decir, definiendo x como el argumento, se tiene: arctan(x)=w, siendo tan(w)=x. La función arcotangente está definida para todo valor de x, y el valor regresado ( w ) está entre -90 y 90 grados (-Pi/2 a Pi/2). Es una función siempre creciente.

Las primera opción es escribir una tabla como las de los libros de matemáticas, donde se proporcionan los valores a cuatro dígitos de precisión del argumento, en incrementos de 0.01, lo cual sería tedioso y de cualquier forma no serviría, porque la función está definida para cualquier valor real, entonces necesitaríamos una tabla bastante grande, hasta un valor para que se considerase "infinito", después del cual la función siempre regresara 90 grados.

Otra opción sería hacer un cálculo aproximado por una serie de Taylor, lo cual tiene el inconveniente de requerir más términos conforme aumenta el argumento x de la función, haciendo variable el tiempo de cálculo.

La solución elegida es intermedia: usar una tabla pequeña que nos da un valor aproximado inicial y luego, mediante identidades y una serie de Taylor limitada a un número fijo de iteraciones, calcular un valor con la precisión requerida.

2. FUNDAMENTO MATEMÁTICO

Para ello supóngase que se aproximará arctan(x) = w como un valor w= r+s, donde r es el valor entero del ángulo w, que puede ser en grados: r = {-80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80} y s es la parte fraccionaria, comprendida entre [0,10) grados. Como la función es simétrica, es decir, arctan(-x)=-arctan(x), bastará con los valores positivos de r. De este modo cubrimos todo el rango posible de la función arcotangente.

Se ha definido que tan(w)= x, o bien que arctan(x)=w, siendo w=r+s. Si en una tabla se tienen los valores de r para un valor x, sólo habrá que encontrar s. La tabla es la siguiente:

            |-------------- x -----------------|-- r---
            0.000000000000000000000000000000000,  0
            0.176326980708464973471090386868619,  10
            0.363970234266202361351047882776834,  20
            0.577350269189625764509148780501957,  30
            0.839099631177280011763127298123181,  40
            1.191753592594209958705308071860420,  50
            1.732050807568877293527446341505870,  60
            2.747477419454622278761664026497670,  70
            5.671281819617709530994418439863960,  80
            

Por ejemplo: se desea calcular arctan (11.0), es decir, x =11.0. De la tabla vemos que r =80 grados. Ahora falta encontrar un valor de s tal que tan (80+ s )= 11.0, porque se definió que arctan(x)= w, o bien que tan(r+s)=x, siendo w=r+s En la realidad, el valor aproximado de w=r+s es 84.8055710922651940062794898192976

Considérese la siguiente identidad:  tan(r+s) = [ tan(r) + tan(s) ] / [1- tan(r)tan(s) ].

Despejando tan(s) = [ tan(r+s) - tan(r) ] /  [1 + tan(r+s)tan(r) ]

Pero se sabe que tan(r+s) = x, es decir: tan(s) = [ x - tan(r) ] /  [1 +  x tan(r) ]

En nuestro caso, x =11.0, r =80, y tan(r) = sen (80)/ cos (80). Como se dijo, las funciones coseno y seno  sí están definidas en nuestro dispositivo. 

Entonces podemos obtener fácilmente tan(s) = [11.0 - tan (80)] /  [1 +  11.0 tan (80)] = 0.084070266

Y se obtiene s por la inversa: s= arctan (0.084070266),

O en general, s= arctan ( [ x - tan(r) ] /  [1 +  x tan(r) ])

Ahora nuestro problema se puede escribir así: arctan (11.0) = 80 + arctan (0.084070266).

O más general: arctan(x)= r+ arctan (k), siendo r un valor de grado entero obtenido por  tablas, y

k =[ x - tan(r) ] / [1 +  x tan(r) ]

Para obtener s = arctan(k), como se ha definido que k está acotada entre 0 y 10,  se puede usar una serie de Taylor.  ¿Por qué no se hizo esto desde el principio, si de todos modos hay que calcular el arcotangente? Porque en el problema original se tendría que calcular arctan(x), donde x es cualquier valor real, lo cual haría que la serie tuviera una cantidad de iteraciones variable, mayor o menor en relación al valor de x, para obtener una precisión deseada. En este caso tenemos que calcular arctan(k),  donde k toma valores entre 0 y 10 grados. Con la función  arcotangente el número máximo de iteraciones para una precisión deseada es el que se requiera para arctan(10).

La serie de Taylor para la función arctan(k) es:

arctan(k)=k - (k^3)/ 3 + (k^5)/5 - (k^7)/7 ... + [ (-1)^n ] [ k^(2n+1) ] /(2n+1),

para las iteraciones i=0,1,2,3... n

Se puede reescribir, con el fin de facilitar la programación:

arctan(k)=k - (k^3)/ 3 + (k^5)/5 - (k^7)/7 ... + ( -1 )[( -1 ) ^ ( n+1 )/ 2 ] (k^n)/n

para las iteraciones i=1,3,5,7... n

La serie regresa el valor en radianes. Para que la tabla también trabaje en radianes, en vez de incrementar r de 10 en 10, se le incrementa en pasos de Pi/18, que equivale a 10 grados. El resultado final ( r+s ) se multiplicará por 180/Pi, para convertir a grados.

Con esta definición, tomando n =10, se realizan 4 iteraciones (n=3,5,7,9) y la precisión es de al menos seis decimales, siendo el error máximo cuando k =10, y nulo cuando k =0. Para nuestro ejemplo arctan(k) =  4.80557106.  Por tanto, arctan (11)=84.80557106

3. EL CÓDIGO

A continuación sigue código en C#

class ArcTanClass 
{ 
    public static double ArcTan(double x) 
    { 
        double []tint={ 
            0.000000000000000000000000000000000,  //tan 0  
            0.176326980708464973471090386868619,  //tan 10  
            0.363970234266202361351047882776834,  //tan 20  
            0.577350269189625764509148780501957,  //tan 30  
            0.839099631177280011763127298123181,  //tan 40  
            1.19175359259420995870530807186042,   //tan 50  
            1.73205080756887729352744634150587,   //tan 60  
            2.74747741945462227876166402649767,   //tan 70  
            5.67128181961770953099441843986396,   //tan 80  
            0 //No significa nada, es para forzar al algoritmo a 
              //dar un valor en caso de que el ángulo sea mayor que 80  
         }; 
        double r=0; 
        //El incremento de cada paso es 180/18= incremento de 10 grados  
        double d = System.Math.PI /18; 
        if (x >= 0) //argumento positivo  
        { 
            //inicializar r en -10 grados para que a la primera iteración se haga cero  
            r = -d; 
            for (int i = 0; i < tint.Length-1; i++) 
            { 
              //Incrementar "d" a "r"  
               r += d;               
              //Si el valor del argumento x está entre tint[i] y tint[i+1],
              // la parte entera es r 
              if (x < tint[i+1])break; 
           } 
        } 
        else //argumento negativo  
        { 
           //inicializar r en 10 grados para que a la primera iteración se haga cero 
           r = d;                
           for (int i = 0; i < tint.Length-1; i++) 
           { 
               //Restar "d" a "r" 
               r -= d;      
               //Aproximación por serie: Si el valor POSITIVO del argumento x
               // está entre tint[i] y tint[i+1], la parte entera es r 
               if (-x < tint[i+1]) break; 
           } 
        } 
        //Cálculo de k mediante identidades:
        double k=(x-System.Math.Tan(r))/(1+x*System.Math.Tan(r)); 
        double kp=k;//Acumulador de la potencia
        double s=k;//Sumatoria
        int u=-1; //Signo: -1 o +1
        //Aproximación por serie: 
        for (int i = 3; i < 10; i = i + 2) 
        { 
            kp = kp * k * k; 
            s += u * kp / i; 
            u = -u; 
        } 
       //Valor de retorno:
       return (r+s)*180/System.Math.PI; 
    } 
    public static void Main() 
    { 
        System.Console.WriteLine(ArcTan(0.577)); 
    } 

} 

Fin del código.


Espacios de nombres usados en el código de este artículo:

System.Console
System.Math

 



ir al índice principal del Guille