Cristalab

Aproximar funciones trigonométricas seno y coseno en Flash

Por: HernanRivas + 27.07.2007

Extendiendo un poco el tema de aproximar funciones matemáticas complejas que comencé en éste post. Agrego ahora un modo de aproximar las funciones seno y coseno que encontré en Internet.

Antes de empezar, quiero aclarar que la información la tomé de éste post de la página Polygonal Labs, que a su vez encontró la información en éste post.

Luego de aclarado eso, vamos a lo verdaderamente interesante.

Quizá lo primero que se nos ocurre para aproximar una función cualquiera (trigonométricas incluidas) es usar una serie de Taylor (recuerdo haberlo intentado).

El problema es que la fórmula es algo complicada y sólo aproxima correctamente cerca del origen (ya que centramos Taylor en 0, por practicidad)



En el ejemplo, se puede ver el seno en verde y el polinomio de Taylor de grado 4 en rojo.

Si hacemos un Zoom, vemos como se comporta fantásticamente hasta π/2 para luego desviarse.



Por supuesto, podríamos tomar Taylor de un mayor órden, pero la idea es hacer las cuentas lo más cortas posibles.

Entonces, es cuando la genialidad surge. Por si no lo habían notado, la función seno en un período se parece a una parábola. Donde:

a = 0
b = 4/π
c = -4/π^2




Pero no es una gran aproximación, incluso, es obvio que es aún peor que usar Taylor.

En éste punto vale aclarar que cuando necesitamos valores negativos, podemos invertir la parábola



Bien, continuando con lo anterior, vamos a buscar una solución al problema de la falta de calidad. Para solucionarlo, observamos que el error es siempre por exceso (es decir, que los valores son mayores o iguales a los verdaderos). Entonces, buscamos una función cuyo error sea por defecto (es decir, valores menores), teniendo cuidado en que sea correcto el valor en 0, π/2 y π



Podemos observar que el módulo del error es mayor al de la aproximación anterior.

Bueno, promediamos ambos valores y obtenemos una aproximación aún mejor. Es más, la aproximación es casi perfecta.



Sólo falta aclarar que las ecuaciones para el coseno son bastante parecidas (apenas un poco más complejas) a las del seno.

Pero basta de teoría. El código sería el siguiente (para la primer aproximación, 14 veces más rápida):

Código :

// Mantenemos el ángulo entre -π  y π
if (x < -3.14159265) {
    x += 6.28318531;
} else if (x >  3.14159265) {
    x -= 6.28318531;
}
// Obtenemos el seno
if (x < 0) {
    sin = 1.27323954 * x + .405284735 * x * x;
} else {
    sin = 1.27323954 * x - 0.405284735 * x * x;
}
// Obtenemos el coseno
x += 1.57079632;
if (x >  3.14159265) {
    x -= 6.28318531;
}
if (x < 0) {
    cos = 1.27323954 * x + 0.405284735 * x * x
} else {
    cos = 1.27323954 * x - 0.405284735 * x * x;
}
}


Para la segunda aproximación (más precisa, pero 8 veces más rápida):

Código :

// Mantenemos el ángulo entre -π  y π
if (x < -3.14159265) {
    x += 6.28318531;
} else if (x >  3.14159265) {
    x -= 6.28318531;
}
// Obtenemos el seno
if (x < 0) {
    sin = 1.27323954 * x + .405284735 * x * x;
    if (sin < 0) {
        sin = .225 * (sin *-sin - sin) + sin;
    } else {
        sin = .225 * (sin * sin - sin) + sin;
    }
} else {
    sin = 1.27323954 * x - 0.405284735 * x * x;
    if (sin < 0) {
        sin = .225 * (sin *-sin - sin) + sin;
    } else {
        sin = .225 * (sin * sin - sin) + sin;
    }
}
// Obtenemos el coseno
x += 1.57079632;
if (x >  3.14159265) {
        x -= 6.28318531;
    }
if (x < 0) {
    cos = 1.27323954 * x + 0.405284735 * x * x
    if (cos < 0) {
        cos = .225 * (cos *-cos - cos) + cos;
    } else {
        cos = .225 * (cos * cos - cos) + cos;
    }
} else {
    cos = 1.27323954 * x - 0.405284735 * x * x;
    if (cos < 0)
        cos = .225 * (cos *-cos - cos) + cos;
    else
        cos = .225 * (cos * cos - cos) + cos;
}


Aclaro que no ubicamos el código en una función ya que consume importantes recursos.

Eso es todo, espero les sirva.

Etiquetas actionscript

Comentarios | Enviar un comentario
Justo lo que estaba buscando, probando!!
Por: bicho_O
Asi de masticadito todo es mas facil, gracias por este post, me ha sido de gran ayuda...
Por: jose bernal_blog
Si al final de todo, mantienes el ángulo entre -PI y PI ... Taylor te lo hubiera resuelto con un error mínimo y controlado. Siempre puedes aproximar Taylor desde otro punto (cualquiera) pero en general desde un múltiplo de PI (para que sea cero igualmente).

Desde el punto de vista matemático esto es ... bueno, no tiene ningún rigor
Por: _CONEJO_blog
Sonrisa excelente, muy bueno de verdad Bien
Por: eldervaz

_CONEJO_blog :

Si al final de todo, mantienes el ángulo entre -PI y PI ... Taylor te lo hubiera resuelto con un error mínimo y controlado. Siempre puedes aproximar Taylor desde otro punto (cualquiera) pero en general desde un múltiplo de PI (para que sea cero igualmente).

Desde el punto de vista matemático esto es ... bueno, no tiene ningún rigor
No, es más un truco matemático que otra cosa, pero las cuentas son más cortas que Taylor de órden 5 (el órden necesario para aproximar con un error menor o igual.
Por: HernanRivas
Esto... o me pierdo en algún sitio, o la función de flash es 3 veces más rápida que la segunda función. Las funciones nativas de Math, por estar escritas directamente en el player suelen ser mucho rápidas que las que el usuario define con ActionScript. O eso o hay algún error aquí:

Código :

x = 1.337;
var t = getTimer();
for (a = 0; a < 10000; a++)
{
   // Mantenemos el ángulo entre -π  y π
   if (x < -3.14159265)
   {
      x += 6.28318531;
   } else if (x > 3.14159265)
   {
      x -= 6.28318531;
   }
   // Obtenemos el seno 
   if (x < 0)
   {
      sin = 1.27323954 * x + .405284735 * x * x;
      if (sin < 0)
      {
         sin = .225 * (sin * -sin - sin) + sin;
      } else
      {
         sin = .225 * (sin * sin - sin) + sin;
      }
   } else
   {
      sin = 1.27323954 * x - 0.405284735 * x * x;
      if (sin < 0)
      {
         sin = .225 * (sin * -sin - sin) + sin;
      } else
      {
         sin = .225 * (sin * sin - sin) + sin;
      }
   }
}
t -= getTimer();
trace(-t);
j = getTimer();
for (b = 0; b < 10000; b++)
{
   y = Math.sin(x);
}
j -= getTimer();
trace(-j);

Por: Zah
Ahora lo checkeo, la verdad, me fié en lo que decía la página y no hice más que corroborar que los valores fueran correctos (es decir, la calidad de la aproximación y no la velocidad).

Ahora voy a ver....
Por: HernanRivas
gracias;

hola:
esta informacion fue muy util muchas gracias, tambien quisiera si nos ayudarias con las series de potencia para el seno, coseno, tangente, logaritmo natural, e elevado a la "x"
Por: hans_blog
necesito los procedimientos de las identidades trigonometricas es urgente
Por: andres_blog
Y se mejora si hacemos una asignación de la función nativa en nuestro código:

Código :

x = 1.337;

j = getTimer();
for (b = 0; b < 10000; b++)
{
   y = Math.sin(x);
}
j -= getTimer();
trace(-j);


r=Math.sin; // esta es la aceleración.
q=getTimer();
for (b = 0; b < 10000; b++)
{
   y = r(x);
}
q -= getTimer();
trace(-q);


En mi equipo de 39ms. a 31ms. para el seno, otras Math mejoran todavía mucho más.
Por: Teseo
agan algo mas efiente estos problemas son muy sencillos
Por: panter_blog
chupmela pinches ñoños vale pito, yo estudio y trabajo y si puedo jajajaaj
Por: daniel_blog
quiero ejemplos
Por: janeth ramirez_blog
esta de peloz y mas ejemplos
Por: jhesica _rock_blog
nesecitamos ejemplos de problemas para a si poder guiarnos mejor
Por: lelsie raquel marin sarav
Deja un comentario
IMPORTANTE

Recuerda ser respetuoso, no insultes a otras personas, ni uses palabrotas, hay una persona al otro lado de la pantalla.

Habla bien, NO ESCRIBAS EN MAYUSCULA TODO, no escribas como en un SMS, evita cosas como "ke", "x q" y demás abreviaciones.

Aquí funcionan las etiquetas de los foros, puedes usar [b] para negrita, [img] para las imágenes, [url] para los enlaces, etc.

Si tienes preguntas técnicas, envíalas mejor al foro.