martes, 30 de agosto de 2016

Capítulo 68: Funciones recursivas en C


Diario de un programador.- Día 167

Funciones recursivas en C

Una función recursiva es una función que se llama a si misma, ya sea en forma directa (que la función se llame a si misma) o en forma indirecta,(que otra función la llame, por ejemplo, que la función 1 llame a la función 2 y la función 2 llame a la 1)
Por lo que estuve leyendo, la finalidad de una función recursiva es crear un ciclo, que en términos computacionales (hablándose de rendimiento) es menos eficiente que si se programa el mismo ciclo utilizando instrucciones for, while o do. ¿Entonces por qué utilizar la recursividad?. La recursividad es útil para representar modelos matemáticos, en otras palabras, un problema matemático (según los entendidos) es más fácil de implementar y leer si se usa la recursividad que su equivalente utilizando la iteración. Un ejemplo clásico (o al menos eso dicen en la web) es calcular el factorial de un número utilizando la recursividad.
Un factorial de un número se representa con un signo de exclamación !. Por ejemplo 5! se lee factorial de 5. Un factorial de un número N, se calcula n * (n - 1) hasta que n sea igual a 1. En nuestro idioma esto significa que si quiero calcular el factorial de 5, entonces se debe calcular 5 * 4 * 3 * 2 * 1. Si quiero calcular el factorial de 3, entonces se multiplica 3 * 2 * 1 etc.

Ejemplo en un código:

#include<stdio.h>

int factorial(int numero);
int factorial(int numero){
  if(numero == 1){
    return 1;
  }
  else{
    return numero * factorial(numero - 1);
  }
}//fin funcion

int main(void){
  int numero;

  printf("Ingresa un numero: ");
  scanf("%d", &numero);

  printf("El factorial de %d es %d\n", numero, factorial(numero));

return 0;

}


La función es la siguiente:

int factorial(int numero){
  if(numero == 1){
    return 1;
  }
  else{
    return numero * factorial(numero - 1);
  }
}

La primera línea de la función:

int factorial(int numero)

Muestra que esta función tiene un tipo de retorno int y que además en su parámetro, solicita un argumento de tipo int.

En las siguientes tres líneas tenemos una instrucción if:

  if(numero == 1){
    return 1;
  }

Esto nos dice que si la variable "numero" (la que recibe de argumento) es igual a 1, entonces retornará un 1 como valor. Esta es la condición que permite que el ciclo finalice, si esta condición está mal planteada, se podría producir un ciclo infinito. Esta condición es conocida como el caso base

En las próximas tres líneas tenemos una instrucción else:

  else{
    return numero * factorial(numero - 1);
  }

Esta instrucción siempre se ejecutará a menos que la variable "numero" sea igual a 1. Esta instrucción devuelve como resultado el valor de la variable multiplicado por un llamado a la función con argumento de la variable "numero" menos 1.

La siguiente modificación al programa anterior, mostrará la secuencia:

#include<stdio.h>

int factorial(int numero);

int factorial(int numero){
  if(numero == 1){
    printf("%d\n", numero);
    return 1;
  }
  else{
    printf("%d * ", numero);
    return numero * factorial(numero - 1);
  }
}//fin funcion

int main(void){
  int numero;

  printf("Ingresa un numero: ");
  scanf("%d", &numero);

  printf("El factorial de %d es %d\n", numero, factorial(numero));

return 0;
}


Otro ejemplo. La serie Fibonacci:

La Serie Fibonacci, tiene la siguiente secuencia: 0,1,1,2,3,5,8,13,21...N.

Empieza con cero y uno. Los números siguientes son el resultado de la suma de los dos números anteriores. Por ejemplo. Después del cero y el uno le sigue un uno, que es el resultado de 0 + 1, luego sigue el 2, que es el resultado de 1 + 1, luego sigue el 3 que es el resultado de 1 + 2, luego 5 que es el resultado de 2 + 3 y así sucesivamente.
La serie fibonacci puede ser definida en forma recursiva como sigue a continuación:
fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)

Si implementamos esa fórmula en un código, quedaría de la siguiente forma:

#include<stdio.h>

int fibonacci(int numero);

int fibonacci(int numero){
  if(numero == 0 || numero == 1){//caso base
    return numero;
  }
  else{
    return  fibonacci(numero - 1) + fibonacci(numero - 2);
  }
}//fin funcion

int main(void){
  int numero;

  printf("Ingresa un numero: ");
  scanf("%d", &numero);

  printf("Fibonacci %d es: %d",numero, fibonacci(numero));

return 0;
}



Esto muestra que 55, se encuentra en la posición 10 de la secuencia. Ejemplo:
0,1,1,2,3,5,8,13,21,34,55

Los números fibonacci rápidamente se hacen grandes. Cada nivel de recursión en la función tiene un efecto duplicador sobre el número de llamadas a las funciones. Esto sería de un orden de 2n Por ejemplo, para calcular la posición número 20 de la secuencia 220, se requieren aproximadamente un millón de llamadas a las funciones, para la posición número 30 se requieren unas mil millones de llamadas. Hacer tantos millones de llamadas a las funciones hace que el resultado demore mucho en llegar (Quizás hasta se cuelgue el programa antes de mostrar algo). Este es un ejemplo que muestra que las soluciones recursivas son menos eficientes que las iterativas. En mi caso particular, hice pruebas con 20 y 30, mostrándome resultados inmediatos. Al hacerlo con 40, se demoró unos 5 segundos en mostrar el resultado. Al hacer la prueba con 50, me aburrí de esperar el resultado. Para tener una idea, con el shell de python hice unos cálculos para tener una idea de cuantas llamadas se requieren para las funciones.   

Lo anterior es conocido como la "complejidad exponencial".

Algo a tener presente es que cualquier problema que puede ser resuelto en forma recursiva, también puede ser resuelto en forma iterativa, por lo tanto se debe evitar la recursión si lo que se busca es rendimiento. Normalmente se escogen soluciones recursivas por un asunto de que es más natural al problema y resulta más fácil de comprender y depurar.
Esto es todo por ahora. Saludos

Gustavo J. Cerda Nilo
Abril 2016, Agosto 2016


http://codigogx.blogspot.cl/2016/08/capitulo-62-numeros-aleatorios-en-c.html http://codigogx.blogspot.cl/2016/10/capitulo-74-busqueda-lineal-en-c.html



viernes, 26 de agosto de 2016

Capítulo 67. Java: Constructores


Diario de un programador.- Día 166


Constructores


Esto es algo que me ha costado un poco entender, así que trataré de explicarlo de la mejor forma posible, además que en la web hay quienes lo explican de una manera y otros que lo explican de otra manera. De todas esas definiciones hice un resumen y sumado a lo puesto en práctica con net beans, llegué a la siguiente conclusión:

Un constructor, es un tipo especial de método que sirve para inicializar (darle un valor) los atributos (variables). Con esto se evita algún posible error ya que los atributos siempre tendrán un dato válido. En otras palabras, esto sirve para que en vez de crear variables así...

nombre;
edad;

Se creen variables así...

nombre = "";
edad = 0;

O con cualquier dato (que sea válido) que nosotros indiquemos. De hecho, si olvidamos crear un constructor, el compilador en forma interna lo crea por defecto.
Lo que hace el compilador es dar un valor por defecto a los datos de tipo primitivo (boolean, byte, char, short, int, long, float y double) y a los de tipo referencia (String y objetos). Los valores por defecto para los tipos de datos son los siguientes:

boolean = false
char = ""
byte, short, int, long, float, double = 0
String, objetos = Null

La diferencia entre ambos tipos de datos, es que los datos de tipo primitivo solo pueden almacenar un dato a la vez.
Otra cosa a tener en cuenta es que las variables locales no se inicializan de manera predeterminada.


Antes de continuar con los constructores me voy a detener en este asunto un momento. Por ejemplo. Tengo dos clases, una llamada Inicio y otra llamada Metodos


En la clase Metodos, tengo un atributo o variable de instancia sin un valor asignado (private int numero). Además, tengo un método llamado Prueba, el cual imprime el valor de dicho atributo. En la clase Inicio, tengo lo siguiente:


Esta clase tiene un objeto de la clase Metodos a la cual llamé "datos". Entonces, este objeto llama al método "Prueba" de la clase Metodos "datos.Prueba()", con la finalidad de mostrar el contenido de ese método. Al ejecutar este programa, esto es lo que ocurre:


Muestra su valor por defecto, lo cual podría provocar un error inesperado en nuestro programa. Lo mismo ocurre si en vez de poner un dato de tipo primitivo, se pone un dato de tipo por referencia, por ejemplo String


Al ejecutar el programa...


Esto no sucede con las variables locales, cualquier intento de utilizar una variable local que no se haya inicializado provocará un error en la compilación. Por ejemplo, si dejo el atributo anterior dentro de un método, entonces se convertirá en una variable local. Veamos un ejemplo de esto.


Inmediatamente me aparecen las advertencias de que existe un error, por lo tanto no se podrá compilar. Si coloco el cursor sobre la línea 5 que marca el error, me dice cual es el problema y justamente el problema es que la variable no está inicializada.


Para corregir esto, solamente hay que inicializar la variable, además de que ya no se puede acceder directamente a esa variable por ser local, por lo que también se debe modificar la línea número 9


Volviendo a los constructores...

Observar la siguiente clase:


Esta clase tiene dos métodos, el primer método "Nombre" solicita el ingreso de nuestro nombre y luego devuelve lo ingresado en la variable "nombre". El segundo método "Antecedentes", muestra lo ingresado en la variable "nombre". A pesar de que este programa funciona, las buenas prácticas de programación (según lo que leí en la web) indican que siempre se debe crear un constructor, aun si nuestros atributos se van a inicializar con cero o con valor null. Por este motivo, es que a esta clase le vamos a agregar un constructor, el cual quedó de la siguiente manera:


Como se puede apreciar, el método sirvió para inicializar el atributo, en este caso su valor de inicio fueron unas comillas dobles, pudo haber sido null pero yo lo dejé así. Otro aspecto a considerar de los constructores es que su modificador de acceso es "public", tienen el mismo nombre de su clase y no tienen valor de retorno.

Al momento de crear un objeto, es posible agregarle un argumento que puede servir para agregar un nombre para un objeto. Hasta el momento, al crear un objeto utilizando la palabra clave "new", se utilizaba un paréntesis vacío, el cual indicaba que no se agregaban parámetros. Por ejemplo, supongamos que tenemos varios cuadernos para distintas materias. Quizás el ejemplo es algo simple, pero servirá para mostrar esto. Entonces, crearemos un objeto indicando el nombre de la materia de ese cuaderno. Ejemplo:


En este caso el objeto tiene como argumento un texto, pero también podría ser un número o la combinación de ambos. Como este objeto tiene un argumento de tipo String, el constructor obligatoriamente debe recibir como parámetro un String. Veamos la clase Metodos.


Como se puede apreciar, el constructor recibe como argumento un String, que en este caso llamé "materia", este String está relacionado con el argumento del objeto "cuaderno" de la clase Inicio, es decir, si el objeto hubiese tenido un valor de tipo entero, entonces el constructor también debería tener como argumento un entero. Ahora, en la clase Inicio realicé la llamada al método "Cuadernos" de la clase Metodos y finalmente ejecuté el programa. El resultado fue el siguiente:


De esta forma podemos crear distintos objetos para las distintas materias de nuestros cuadernos, sin tener que modificar la clase Metodos.


Esto es todo por ahora, hasta la próxima.

Gustavo J. Cerda Nilo
Abril 2016, Agosto 2016



domingo, 21 de agosto de 2016

Capítulo 66: Moviendo figuras en Small Basic. Parte 2


Diario de un programador.- Día 165

Segunda parte de la clase anterior. Continuaremos con el resto de los ejercicios.


3.- Código para evitar que la figura salga de la ventana

Dentro de las subrutinas "arriba, abajo, izquierda y derecha" agregué el siguiente código. Ejemplo:

If x <= 0 Then ' para no salir del limite de la pantalla
    Shapes.Move(circulo, 0, y)
    x = 0
EndIf

Esa condición se encuentra dentro de la subrutina "izquierda". Como se puede ver, la condición dice que si "X" es menor o igual a cero, entonces la operación "Move" hace que la figura se mantenga en su posición y no siga avanzando, esto debido a que dejé su valor en cero (circulo, 0, y) La variable x = 0 que destaqué a continuación es importante para evitar un pequeño "bug" o "glitch" (bienvenido a mi mundo jajaja) Lo que sucedía es que al llegar al límite de la pantalla, efectivamente la figura no se movía, pero al presionar otra tecla, la figura se "transportaba" fuera de la pantalla (prueba a quitar esa X para entender lo que digo) Esto sucedía porque al seguir presionando la tecla en esa dirección, la variable x iba acumulando valores. Así, al momento de presionar en otra dirección, la figuraba se desplazaba al lugar donde la variable x había quedado. Al ver esto (Lo cual me costó varios minutos en entender que estaba pasando) decidí que lo mejor era igualar la variable x a cero, para que así no acumule valores a pesar de seguir presionando en esa dirección.


4- Agregar otra figura en pantalla, que se mueva sola y que cambie de dirección al azar.
Para este ejercicio yo agregué un cuadrado azul. Luego me puse a analizar el ejercicio y se me ocurrió que para hacerlo cambiar de dirección debía ocupar la operación Random del objeto Math. Lo que me tuvo de cabeza un tiempo fue el encontrar alguna operación que me permita "activar" automáticamente un evento. Fue entonces que me puse a investigar el objeto Clock, el cual permite acceder al reloj del sistema. El objeto Clock, tiene la propiedad "second" el cual obtiene el segundo actual. Como los segundos cambian constantemente, entonces se me ocurrió ocupar esta propiedad para mi trabajo. La forma en que ocupé esta propiedad, fue la siguiente: Hice un ciclo infinito, donde cada dos segundos se llamaba a una subrutina, la cual modifica la posición X,Y de la figura. Una vez modificada la posición, se emplea la operación "animate" para desplazar la figura a ese lugar.
El ciclo infinito se codificó de la siguiente manera:

azar = 1
 While azar = 1
     reloj = Clock.Second
     If Math.Remainder(reloj, 2) = 0 Then
         MovCuad()' <-- Subrutina para mover la figura
      EndIf
 EndWhile

La subrutina para mover la figura quedó de la siguiente manera:

Sub MovCuad
   azarX = Math.GetRandomNumber(500)
   azarY = Math.GetRandomNumber(500)
   Shapes.Animate(cuadrado,azarX,azarY,2000)
 EndSub

Esto es todo. Ahora colocaré el código completo:

imagen = "G:\fondo.png"
GraphicsWindow.DrawImage(imagen,0,0)
GraphicsWindow.BrushColor = "Red"

x = 300
y = 200

circulo = Shapes.AddEllipse(50,50)
Shapes.HideShape(circulo)
Shapes.Move(circulo,x,y)
Shapes.ShowShape(circulo)

GraphicsWindow.KeyDown = presionar

Sub derecha
  x = x + 2
  Shapes.move(circulo,x,y)
 
  If x >= 574 Then ' para no salir del limite de la pantalla
    Shapes.Move(circulo, 574, y)
    x = 574
  EndIf
 
  Posicion()
EndSub

Sub izquierda
  x = x - 2
  Shapes.Move(circulo,x,y)
 
  If x <= 0 Then ' para no salir del limite de la pantalla
    Shapes.Move(circulo, 0, y)
    x = 0
  EndIf
 
  Posicion()
EndSub

Sub abajo
  y = y + 2
  Shapes.Move(circulo,x,y)
  If y >= 392 Then ' para no salir del limite de la pantalla
    Shapes.Move(circulo, x, 392)
    y = 392
  EndIf
 
  Posicion()
EndSub

Sub arriba
  y = y - 2
  Shapes.Move(circulo,x,y)
 
  If y <= 0 Then ' para no salir del limite de la pantalla
    Shapes.Move(circulo, x, 0)
    y = 0
  EndIf
 
  Posicion()
EndSub

Sub presionar
  flecha = GraphicsWindow.LastKey
 
  If flecha = "Right" Then
    derecha()
  EndIf
 
  If flecha = "Left" Then
    izquierda()
  EndIf
 
  If flecha = "Down" Then
    abajo()
  EndIf
 
  If flecha = "Up" Then
    arriba()
  EndIf

EndSub

'POSICION DEL CIRCULO
GraphicsWindow.DrawRectangle(520,15,100,50)'cuadro negro

GraphicsWindow.BrushColor = "white"'
GraphicsWindow.FillRectangle(521,16,98,48)'cuadro blanco
 
GraphicsWindow.BrushColor = "red"
GraphicsWindow.DrawText(530, 20, "POS X: ")
GraphicsWindow.DrawText(530, 40, "POS Y: ")

GraphicsWindow.DrawText(580, 20, x)
GraphicsWindow.DrawText(580, 40, y)

Sub Posicion
  GraphicsWindow.BrushColor = "white"'tuve que parchar, para no hacer clear
  GraphicsWindow.FillRectangle(521,16,98,48)'con un cuadro blanco ;)
 
  posX = Shapes.GetLeft(circulo)
  posY = Shapes.GetTop(circulo)
 
  GraphicsWindow.BrushColor = "red"
  GraphicsWindow.DrawText(580, 20, posX)
  GraphicsWindow.DrawText(580, 40, posY)
  GraphicsWindow.DrawText(530, 20, "POS X: ")
  GraphicsWindow.DrawText(530, 40, "POS Y: ")
EndSub

'OTRA FIGURA
 GraphicsWindow.BrushColor = "Blue"
 cuadrado = Shapes.AddRectangle(50,50)
 azar = 1

 While azar = 1
   reloj = Clock.Second
   If Math.Remainder(reloj, 2) = 0 Then
     MovCuad()
   EndIf
     
 endWhile

 Sub MovCuad
   azarX = Math.GetRandomNumber(500)
   azarY = Math.GetRandomNumber(500)
   Shapes.Animate(cuadrado,azarX,azarY,2000)
 EndSub


Una vez que se ejecuta el código, se puede controlar el circulo con las flechas del teclado y la otra figura parecer tener "vida propia", desplazándose como si fuese un bicho en nuestra pantalla.


Gustavo J. Cerda Nilo
Marzo 2016, Agosto 2016



C++ El apuntador This

El apuntador This En C++, cada objeto tiene acceso a su propia dirección a través de un puntero o apuntador denominado This. Lo...