2.1 Funciones matemáticas

Hasta ahora, vimos cómo realizar muchas operaciones diferentes mediante programas. Todas estas operaciones se pueden combinar, generando programas cada vez más complejos.

Muchas de estas operaciones, pueden ser necesitadas en más de un programa: para evitar tener que escribirlas de vuelta en cada programa que las necesita, existen las funciones o subprogramas.
Una función es un programa en sí mismo: tiene sus propias instrucciones, variables, etc. Puede ser ejecutada desde otro programa o función.

El C++ incluye cientos de funciones para realizar las operaciones más diversas, que puedan ser necesarias en nuestros programas. Estas funciones ya están programadas y no es necesario conocer sus instrucciones internas: Nos bastará con saber qué datos debemos enviarles y qué resultado nos devuelven.
Las ventajas de utilizar estas funciones, además del ahorro de tiempo al no tener que escribirlas, son:
- Libres de errores: Las funciones ya fueron probadas en millones de casos diferentes, y es altamente improbable que encontremos un error en ellas.
- Eficientes: Están programadas para realizar las operaciones de la forma más rápida posible.

Las funciones no son parte del núcleo del C++, esto significa que cada versión de C++ puede tener diferentes funciones, aunque la mayoría de las funciones son comunes a casi todas las versiones. Al no estar en el núcleo del C++, debemos agregarlas al momento de compilar el programa o no funcionará, al no poder entender el compilador la llamada a la función. Las funciones están en archivos aparte llamados bibliotecas (libraries en inglés, a veces traducido como "librerías")

Para utilizar algunas de estas funciones, debemos indicarle al compilador que incluya el archivo deseado en nuestro programa, con la directiva

#include  <nombre  del archivo>

 Así, la instrucción     #include <iostream> que agregamos al principio de cada programa, no es otra cosa que un aviso al compilador para que incluya el archivo iostream, que es el que contiene las definiciones de las funciones cin y cout

 FUNCIONES MATEMÁTICAS

Las primeras funciones de biblioteca que vamos a ver son las llamadas funciones matemáticas, ya que se utilizan para resolver distintos cálculos. Estas funciones están incluidas en el archivo de C++ <cmath> y éste es el nombre que debemos incluir en nuestros programas para usar alguna de estas funciones:

#include <cmath>


La primera función que vamos a ver es sqrt(). Esta función permite calcular raíces cuadradas positivas (square roots en inglés, de ahí el nombre de la función.), y su prototipo es el siguiente:

double sqrt(double);

El prototipo de una función nos indica qué tipos de datos envía y recibe. Una función siempre envía como respuesta un único valor al programa que la llamó. En este caso ese dato es de tipo double (valor real de doble precisión) y lo indica la primera palabra del prototipo.
Luego viene el nombre de la función, y por último, entre paréntesis, la lista de parámetros, o valores que recibe la función. En este caso, es uno sólo y también de tipo double.
Para ver como funciona, veamos el siguiente ejemplo:



#include "stdafx.h"
#include <iostream>
using namespace std;
#include <cmath>
void main()
{
       double a,b;

       cout<<"Ingrese un número:";
       cin>>a;
       b = sqrt(a);
       cout<<"Su raíz cuadrada es "<<b;
}


Es un programa sencillo, tiene los encabezados habituales, vemos que en la cuarta línea ya está la instrucción de inclusión de funciones matemáticas.
El programa define dos variables de tipo double, a y b; y luego pide al usuario que ingrese el valor de a. Una vez que se llega a la instrucción "b=sqrt(a)", se detiene momentáneamente la ejecución del programa principal (main) y se llama a la función sqrt con el valor de la variable a como parámetro.
La función realiza las operaciones que sean necesarias y devuelve al programa principal el valor de la raíz cuadrada del valor que recibió. Una vez allí, el programa principal reanuda su ejecución, guardando el valor recibido en la variable b. Luego muestra dicho valor y finaliza el programa.

- Se debe tener en cuenta que la función sqrt sólo puede calcular un resultado para los valores de parámetros para los que esté definida la raíz cuadrada matemática. Es decir, que si intentamos calcular la raíz cuadrada de un número negativo, la función dará un error y se interrumpirá la ejecución del programa. Es necesario validar el parámetro antes de llamar a la función.
- Como indicamos anteriormente la función sólo devuelve la raíz positiva, no la negativa.


Las siguiente función es la función pow:

double pow (double, double)

Esta función recibe dos parámetros que representan la base y el exponente y devuelve como resultado el valor de la base elevado al exponente indicado. Así, por ejemplo:

pow(5,2) dará como resultado 25.
pow(2,5) dará como resultado 32.

El exponente puede ser cualquier valor racional, por lo que se pueden calcular inversas y raíces de cualquier orden:

pow(16,1/4) dará como resultado 2.

pow(-49,1/2) dará un error.

La función pow permite calcular cualquier raíz, incluyendo raíces cuadradas. Entonces, ¿qué función conviene usar cuando queremos calcular  una raíz cuadrada, sqrt() ó pow() ? La recomendación es usar sqrt(), que ha sido diseñada específicamente para raíces cuadradas y está optimizada para ello. La función pow() es más genérica y puede demorar más tiempo en calcular las raíces.


Las siguientes funciones que vamos a ver son:


double sin(double)
double cos(double)
double tan(double)

Estas funciones, como ya se imaginarán, devuelven los valores del seno, el coseno y la tangente del ángulo que reciben como parámetro. Se debe tener en cuenta que el valor del ángulo dado estará en radianes. (Recordemos que 3,14 radianes = 180 grados)
Entonces:

sin(0) dará como resultado 0,
sin(3.14/2) dará como resultado 1,
cos(3.14) dará como resultado -1,

El siguiente programa pide al usuario que ingrese el valor de un ángulo y calcula sus funciones trigonométricas:



#include "stdafx.h"
#include <cmath>
#include <iostream>
using namespace std;
#define PI 3.1416
void main()
{
       float ag, ar, s,c,t;
       cout<< "Ingrese el angulo en grados";
       cin>>ag;
       ar = ag * PI /180;
       s = sin(ar);
       c = cos(ar);
       t = tan(ar);
       cout<<"El seno es "<<s<<endl;
       cout<<"El coseno es "<<c<<endl;
       cout<<"La tangente es "<<t<<endl;
}

 
En la línea 5, vemos la directiva  
   #define PI 3.1416
Esta directiva no es parte del programa sino que es una indicación al compilador (al igual que los #include) que le indica que siempre que en el programa aparezcan las letras PI las reemplace por 3.1416. Este reemplazo se hace al momento de compilar el programa. Así, si en el programa ponemos las letras PI, serán remplazadas por el valor numérico en todas las partes donde aparezcan.
De esta manera podemos definir una constante, que no podrá cambiar de valor en todo el programa.

En el programa, el usuario ingresa el valor en grados de un ángulo en la variable ag. Luego, en la variable ar se calcula su equivalente en radianes, y con tres llamadas a las funciones sin( ), cos( ) y tan( ), se calculan los valores trigonométricos buscados.

Así como tenemos estas tres funciones, también existen en C++ las funciones:

double asin(double)
double acos(double)
double atan(double)
que son las inversas de las anteriores: reciben un valor numérico que represente el seno, coseno o tangente de un ángulo, y devuelven el valor del ángulo correspondiente en radianes.
Estas funciones nos permiten obtener el valor de pi con la mayor precisión posible: Si sabemos que cos(180) = -1 y que 180 grados es igual a pi radianes, entonces:

 cout<<acos(-1.0)

Nos devolverá el mejor valor de pi que puede calcular la máquina.


Las últimas funciones que comentaremos aquí son:


double exp(double)
double log(double)

Estas funciones permite calcular e a la x (El número de Euler elevado a una potencia x), y el logaritmo en base e de un valor, respectivamente.
Se debe tener en cuenta que anuque se escriba log( ) no representa la función matemática de logaritmo en base 10, sino al logaritmo natural o neperiano.
Así, las instrucciones:

cout<<exp(1.0)<<endl;
cout<<log(10.0)<<endl;
cout<<log(exp(1.0))<<endl;

darán como resultados  2,7182; 2,3026 y 1 respectivamente.

Hay muchas otras funciones matemáticas en C++; su funcionamiento es siempre análogo a las ya vistas y su comprensión es muy sencilla. Se debe recordar siempre que en los valores para los que no está definida la función matemática, la función en C++ dará un error al ejecutarla.




2.2 Funciones para azar

Una de las posibilidades más interesantes de la programación es permitir la generación de resultados al azar.
Esta posibilidad tiene múltiples aplicaciones, desde la programación de juegos que tengan algún componente aleatorio, hasta el desarrollo de algoritmos de seguridad, para encriptación de claves.

Para ello, en C++ se utiliza la función rand, cuyo prototipo es:

      int rand()

Esta función no recibe ningún parámetro, y devuelve un valor entero. Dicho valor entero estára entre 0 y el máximo valor posible de un entero. Este valor máximo es como mínimo 32767; pudiendo ser hasta 2147483647.

Veamos un ejemplo de su uso:

 #include <iostream>
using namespace std;
void main()
{
    int i,n;
    for (i=1;i<=10;i++)
    {
        n = rand();
        cout << n <<endl;
    }
    cin>>n;
}

Este programa genera y muestra 10 números al azar, por ejemplo:






Como vemos, los números generados están en el rango 0-32767. Si queremos llevar este número a otro rango de valores, debemos realizar ciertas operaciones. Para convertir el número al rango 1-6 se debe modificar la línea correspondiente por:

    n = rand()%6+1;

Así la operación %6 divide el número generado por 6 y toma el resto de dicha división, lo que da un número entre 0 y 5. Sólo falta sumar 1 y se obtienen números en el rango 1-6.

El siguiente programa simula los números de una ruleta hasta que sale el cero:

#include <iostream>
using namespace std;

void main()
{
    int n;
    long i;
    n=1;
    do
    {
        n = rand()%37;
        cout << n << "   ";
        for (i=0; i<100000000; i++);
    }
    while (n!=0);
    cin>>n;
  }


 Como ya se habrán dado cuenta a esta altura, el programa devuelve siempre la misma serie de números aleatorios cada vez que se ejecuta. Esto puede ser útil si se quiere reproducir una serie varias veces para ver cómo responde un programa, pero no sirve de mucho si estamos simulando un juego de azar, ya que en ese caso sólo sería necesario memorizar la serie para ganar siempre.
Para evitar esto, debemos ver cómo se generan los números en la función rand. La computadora no puede generar números realmente aleatorios, lo que hace para causar la apariencia de azar es tomar un número inicial o semilla de la serie y le aplica una serie de operaciones matemáticas lo suficientemente complejas como para que no sean deducibles a partir del resultado. El valor así obtenido es el que devuelve la función rand y se convierte en la semilla para el nuevo valor. Así, la próxima vez que llamemos a la función, tomará este valor como semilla, le aplicará los cálculos y nos dará como resultado el tercer valor de la serie. Los valores así obtenidos no son realmente aleatorios, sino pseudoaleatorios.


Para evitar esto, utilizamos la función srand:

     void srand(int);
 
Esta función recibe como parámetro un valor entero que será la nueva semilla de la serie.
Ahora bien, podemos cambiar la semilla de la serie, pero ¿cómo hacemos que sea un valor aleatorio? No podemos pedir al usuario que ingrese su valor: No tardará en descubrir que siempre que ingresa el mismo número, obtiene la misma serie. Dicho valor deber obtenerse de la computadora. Para ello podemos utilizar la función time, incluida en el archivo <ctime>. Esta función devuelve un valor entero, y cuando se la invoca con el parámetro NULL, devuelve un valor que representa la cantidad de segundos transcurridos desde el tiempo "cero" del sistema (generalmente, el 01/01/1970). Ahora sí, tenemos un valor realmente aleatorio para comenzar nuestra serie, sólo agregando un par de líneas al programa:

 #include <iostream>
using namespace std;
 #include <ctime>

void main()
{
    int n;
    long i;
    srand(time(NULL));
    n=1;
    do
    {
        n = rand()%37;
        cout << n << "   ";
        for (i=0; i<100000000; i++);
    }
    while (n!=0);
    cin>>n;
  }


  

2.3 Funciones de Usuario

En las secciones anteriores, vimos cómo utilizar las funciones que el C++ nos brinda para nuestros programas.
Ahora, vamos a ver cómo podemos crear nuestras propias funciones, que puedan ser llamadas desde cualquier lugar del programa. Esto nos permitirá escribir el código de estas funciones una sola vez y utilizarlas tantas veces como sea necesario.


Veamos un ejemplo sobre cómo utilizar una función para calcular el factorial, problema que vimos en el capítulo anterior.
En el programa, utilizamos la función factorial( ), que es la que realiza las operaciones. Esta función recibe un parámetro de tipo int (en este caso, el contenido de la variable n) y devuelve un valor, también de tipo int (que en este caso, se guarda en la variable fact).
Esta información acerca de los parámetros, debe ir en el prototipo de la función, que va antes del programa main( ).Como la función puede estar o no incluida en el mismo archivo, es necesario decirle al compilador que existe una función llamada factorial y qué cantidad y tipo de parámetros posee. Esa es la función del prototipo.




#include "stdafx.h"
#include <iostream>
using namespace std;
int factorial (int);

 void main()
{
       int n, fact, i;
       cout<<"Ingrese un número:";
       cin>>n;
       fact = factorial(n);
       cout<<"El factorial de "<<n<<" es "<<fact<<endl;
}


En este ejemplo, luego de los #include, agregamos el prototipo de la función: Una función llamada factorial que recibe un parámetro int y devuelve un valor int.
Luego se ejecuta el programa: se declaran las variables y se pide al usuario que ingrese un número que se guarda en la variable n. Llegado este punto, en la línea siguiente, se ejecuta la función factorial con el valor de la variable n. El valor devuelto por la función, se guarda en la variable fact, y se muestra al usuario.

El código de la función, que debería ir a continuación del programa principal, es el siguiente:



int factorial (int n)
{
       int f,i;
       i=1,
       f= 1;
        while (i <= n)
       {
             f = f * i;
             i = i+1;
       }
        return f;
}


Si analizamos el código, vemos que se recibe un parámetro de tipo entero y se define dos variables más que se inicializan en 1. Luego se realiza un ciclo while que multiplica todos los números de 1 hasta el número n.
Finalmente, la instrucción return devuelve el valor de f al programa que llamó a la función.

- Las funciones son subprogramas independientes del programa principal, que tienen sus propias variables. En este caso el parámetro se llama n, igual que la variable n del programa principal. Puede llamarse igual o distinto, pero son dos variables distintas: Cualquier cambio que haga a la variable n de la función, no afecta a la variable n del programa principal. La única forma que tiene la función de pasar un dato al programa principal, es mediante la instrucción return, que devuelve un único valor, del tipo de datos que corresponda a la función.

- Si analizamos el programa principal de cualquier ejercicio, vemos que tiene la estructura de una función:




 void main()
{
       .....
       .....
       .....
}


Es una función que no recibe parámetros (por eso abro y cierro los paréntesis sin nada dentro) y que no devuelve ningún valor (por eso es de tipo void). En algunos casos se define de tipo int, y el valor que devuelve al sistema operativo indica si hubo algún error al ejecutar el programa. Pero en esencia es una función más, lo único particular que tiene es el nombre: El compilador buscará la función main y será la que comience a ejecutar.