Descargar

Introducción a los algoritmos (página 2)

Enviado por Higinio Bellón Corvo


Partes: 1, 2, 3

La idea de seleccionar el mejor algoritmo entre un grupo mayor o menor de variantes posibles, es relativa. Los principiantes suelen irse por la vía del que menor cantidad de pasos tiene, buscando una mayor velocidad de ejecución. Este aspecto aunque importante, no es el único a tener en cuenta, además se puede afirmar que no siempre el más pequeño es el más rápido. En computación existe una regla que se cumple generalmente y es que cuando se ahorra tiempo se gasta más espacio y viceversa.

? Ilustremos esta afirmación a través de un ejemplo. Se muestran dos fragmentos de programas elaborados en el lenguaje de programación Turbo Pascal, los cuales realizan un cálculo con 100 valores diferentes:

edu.red

Podemos apreciar que el fragmento B duplica al A (en cuanto a la cantidad de líneas), sin embargo se ejecuta mucho más rápido ya que calcula 800*A y 20*B una sola vez en lugar de las 100 que lo hace el fragmento A.

No obstante, a continuación se relacionan algunos consejos muy útiles y que de modo general nos permitirán encontrar sino el mejor, al menos un buen algoritmo de solución:

  • Escoger uno que no sea ni muy grande ni muy lento.

  • Que sea claro y se puedan entender fácilmente las operaciones que se realicen (no se puede olvidar que más tarde tendremos nosotros mismos que darle mantenimiento a estos algoritmos).

  • Escoger uno que dé el resultado correcto (criterio este considerado como el único que tiene que cumplirse de forma absoluta).

Si después de la selección, encontramos varios algoritmos que cumplen los puntos anteriores y tenemos dudas de cual elegir, podemos hacer lo siguiente: los ponemos a trabajar (entiéndase esto por entrarle valores a los datos primarios y evaluar los datos finales o de salida) a todos bajo situaciones similares a las que realmente se enfrentará el que nosotros determinemos. Durante esta prueba pasaremos a medir las características cuantitativas y evaluar las cualitativas de cada uno por separado. Al final de esta tarea, cuando confrontemos los resultados, estaremos mejor preparados para tomar una decisión.

Se nos puede dar el caso también que dentro del conjunto a seleccionar, uno de los algoritmos haya sido confeccionado por nosotros. Es aconsejable aquí acogernos siempre a nuestra creación, incluso aunque esta posea pequeñas desventajas con respecto a los otros. En el caso de que las desventajas fueran muy notables, entonces habría que empezar a pensar en un rediseño de nuestro algoritmo u optar por desechar el nuestro y seleccionar otro mejor.

Es recomendable escoger un algoritmo diseñado y confeccionado por nosotros, ya que es una solución que le hemos dado a un problema producto de un estudio y análisis realizado por nosotros mismos. Por lo tanto, a la hora de trabajar con él, de ejecutarlo, o de hacerle pequeñas o grandes modificaciones, vamos a estar más familiarizados y más seguros que si nos encontráramos en presencia de un algoritmo hecho por cualquier otra persona. Si nos viéramos obligados a seleccionar un algoritmo que no haya sido confeccionado por nosotros, debemos estudiarlo a fondo y tratar de entender cual fue el análisis que se hizo. En caso de que podamos interpretar bien cada paso y sepamos el por qué de cada dato usado, habremos aprendido algo y estaríamos en una situación muy parecida a como si lo hubiésemos diseñado nosotros mismos. Muchos programadores hacen de esto una técnica para adquirir experiencia y aumentar sus conocimientos.

Después de leer estos análisis vemos como la selección del mejor algoritmo es relativa y es un criterio que podrá variar mucho dentro de un grupo de diseñadores y programadores.

Características de los algoritmos

La confección de algoritmos es un trabajo de creación. No existen procedimientos estrictos que nos permitan elaborar algún algoritmo necesario. Todo, empezando por la elección de las palabras a utilizar y terminando por la estructura, depende completamente de las capacidades, gustos y habilidades creadoras del individuo. Lógicamente, esta "libertad de creación" presenta un número grande de ventajas y desventajas. No nos detendremos ahora a realizar un análisis detallado de cada una de ellas puesto que este es un tema muy discutido y sobre el cual los criterios difieren mucho. Sólo se expondrán dos ejemplos (una ventaja y una desventaja) donde la balanza de criterios se ha inclinado más a favor que en contra.

  • 1- La falta de procedimientos generales y de reglas a la hora de confeccionar cualquier algoritmo tiene una importante ventaja y es que, todo el que desee puede plantear su solución a un problema determinado sin necesidad de estudiar un lenguaje de programación en específico y sin tener que memorizar una serie de palabras (muchas de ellas llamadas dentro de estos lenguajes como palabras reservadas) y reglas estrictas. Esto permite también que el que confecciona pueda concentrarse más en determinados aspectos del problema a solucionar y no pierda tiempo estructurando su trabajo ni adentrándose en los detalles del lenguaje empleado.

  • 2- Como desventaja notable se plantea la poca (en algunos casos ninguna) universalidad y el muy mal entendimiento del algoritmo creado, o sea que lo que hacen unos muchas veces no lo entienden otros. Para evitar este problema se han desarrollado los llamados lenguajes de descripción de algoritmos.

Los lenguajes de descripción de algoritmos son muy fáciles de aprender ya que se basan en esquemas o símbolos que combinados con palabras y frases del lenguaje natural conforman la secuencia de pasos. Ejemplo de esto tenemos el llamado diagrama de bloques o como también se conoce diagrama de flujos, en el cual se va a representar cada operación con un símbolo diferente (cada uno recibe el nombre de bloque) en cuyo interior van a estar los datos con que se opera. Los bloques están conectados entre sí por flechas que indican el flujo de la ejecución. En el Capítulo III se estudian de forma detallada las características de este lenguaje. Otro ejemplo de lenguaje simbólico lo constituyen los estructugramas. Estos son empleados para describir pequeños módulos puesto que su estructura se complicaría demasiado al tratar de resolver un problema grande. No presentan flechas ni líneas de flujo y todos sus pasos se encierran dentro de un mismo paralelogramo donde son separados según correspondan.

Para resolver problemas largos se han creado lenguajes de descripción que emplean órdenes similares a algún lenguaje de programación de computadoras, pero donde no se siguen todas las rigurosas reglas y además se emplean frases del lenguaje natural. Estos son muy utilizados en la práctica debido a su fácil conversión cuando van a ser ejecutados en una computadora. Cada lenguaje de descripción ha sido adaptado por los propios programadores según sea el lenguaje de programación con que trabajen, por eso podemos ver lenguajes con una estructura que tiende al Turbo Pascal, otros al C++ y así.

El destino de todo algoritmo confeccionado en cualquier lenguaje de descripción será siempre su traducción a un lenguaje de programación para que pueda ser ejecutado por una máquina computadora. Estos algoritmos después de traducidos reciben el nombre de programas, los cuales tampoco son ejecutados directamente por la computadora, sino que esta última convierte todas esas instrucciones en códigos directamente entendibles por ella.

Una utilidad muy importante que se les ha dado a estos lenguajes de descripción es en los programas de educación, ya que estos han sido incorporados a asignaturas de introducción a la programación con el acertado propósito de enseñar a programar a los alumnos antes de que puedan trabajar con los lenguajes de programación.

Todos los algoritmos, independientemente del objetivo con que fueron diseñados y construidos, nos van a permitir distinguir en su interior tres partes, las cuales no siempre las podremos delimitar con claridad, estas son:

  • Introducción de la información (Entrada de datos): Constituida por todas aquellas instrucciones que toman datos de un dispositivo externo, almacenándolos para que puedan ser procesados.

  • Procesamiento de la información: Está formado por las diferentes instrucciones que modifican los objetos a partir de su estado inicial hasta el estado final.

  • Presentación de la información (Salida de los resultados): Es el conjunto de instrucciones que toman los datos finales y los envían a los diferentes dispositivos externos de salida.

A la hora de construir un algoritmo tenemos que tener en cuenta algunas características propias que siempre tienen que estar presentes, ellas son:

  • Objetividad o Finalidad: Cada algoritmo elaborado tiene como objetivo dar solución a un tipo de problema determinado y su resultado tiene que constituir dicha solución.

  • Precisión: Consiste en obtener siempre el mismo resultado, aplicando al algoritmo el mismo juego de datos iniciales. Por esta razón se dice que son unívocos.

  • Orden: La secuencia de pasos del algoritmo tiene un orden establecido para ejecutarse, en el caso de que dicho orden sea modificado casi siempre se deja de obtener el resultado esperado.

  • Efectividad: Un algoritmo es efectivo cuando todas sus operaciones puedan ser desarrolladas por una persona utilizando lápiz y papel.

  • Finitud: Todo algoritmo tiene que ser finito, o sea, que cuando lo ejecutemos, después de una x cantidad de operaciones, este debe finalizar. Es lógico pensar lo absurdo que sería elaborar un algoritmo cuya ejecución no terminase nunca.

  • Entrada y Salida: Podemos afirmar que los algoritmos pueden tener datos de entrada o pueden prescindir de ellos. Para ejecutar un algoritmo que permita calcular si un número determinado es primo necesitaríamos "entrarle" dicho número, sin embargo, si quisiésemos elaborar otro algoritmo que halle los primeros 200 números pares positivos no tendríamos entonces la presencia de los datos de entrada. No pasa lo mismo con los datos de salida, pues estos siempre tendrán que estar presentes en mayor o menor medida ya que mostrarán la solución del problema resuelto. Cabe señalar aquí que los datos de salida de un programa pueden estar representados de diferentes formas y formatos, así vamos a poder encontrar salidas en forma de textos, de gráficos (estos últimos pueden ser de barras, de línea, del tipo pie, u otros, en dependencia de lo que se quiera informar), salidas esquematizadas o agrupadas en forma de tablas. El diseño de los datos de salida va a estar en correspondencia con el nivel de análisis que se desee realizar. Al estudio de la presentación de los datos se le llama diseño de salidas y se puede decir que ha alcanzado un gran desarrollo en los últimos tiempos puesto que antes, los usuarios sólo podían ver los resultados de sus algoritmos en formatos estáticos. En cambio, hoy día se confeccionan programas donde, a la hora de imprimir, el usuario final puede personalizar de forma muy variada el formato con el cual se mostrarán sus datos finales.

Las entradas y salidas van a permitir el flujo de información existente entre el exterior y el algoritmo, constituyendo además un elemento de suma importancia para la comunicación entre el hombre y la máquina, donde éste brinda la información necesaria para obtener de esta última la respuesta requerida. Este flujo se realiza a través de los datos, los cuales, atendiendo a la variación de la información que contienen se pueden clasificar en:

  • Datos constantes: Aquellos cuyo contenido, tomado una sola vez dentro de la secuencia de pasos del algoritmo, nunca va a variar durante la ejecución de este. Todas las veces que ejecutemos el algoritmo estos datos brindarán la misma información. Ejemplo de dato constante lo podemos encontrar en un algoritmo que resuelva algún problema de cálculo matemático donde se haga uso del valor de ?. Dicho dato, que aproximadamente es 3,1415… nunca va a variar su valor ya que constituye una constante matemática.

  • Datos variables: Estos son los más usados dentro del análisis y solución de problemas, ya que nos permiten utilizar juegos de datos diferentes para un mismo programa o algoritmo. Estos pueden variar su contenido cuantas veces se necesite.

Los datos dentro de los algoritmos van a estar representados por un nombre en lugar de su valor explícito. Este nombre no va a ser más que la dirección simbólica de una localización de la memoria de la computadora en la cual se van a guardar dichos datos. Aunque los diferentes lenguajes de programación tienen sus reglas estrictas y particulares para que los programadores creen estos nombres, normalmente estarán compuestos por una combinación de caracteres alfabéticos o alfanuméricos. Un lenguaje que presentaba grandes limitaciones con los nombres dados a las variables y a las constantes lo constituyó el FORTRAN. Este fue un lenguaje de mucha utilización en cálculos científicos y de ingeniería, su nombre proviene de FORmula TRANslation (traducción de fórmulas) y tenía gran parecido con la notación matemática común.

En FORTRAN los caracteres que conformaban el nombre de los datos sólo podían ser las letras mayúsculas del alfabeto inglés y en el caso de que se usaran caracteres alfanuméricos entonces dicho nombre debía comenzar siempre con una letra. Cabe destacar aquí la extraordinaria limitante en cuanto a la longitud del nombre ya que no podía exceder los 6 caracteres. También se establecían reglas según el valor almacenado, por ejemplo, si era un dato numérico entero (números que no tienen parte fraccionaria) el nombre debía empezar con una de las letras I, J, K, L, M ó N.

Ambos tipos de datos, los variables y los constantes, ya vistos anteriormente, podrán encontrarse en cualquier parte del algoritmo realizando diferentes funciones. Atendiendo a estas últimas vamos a poder dividirlos en tres grandes grupos funcionales, que son:

  • Primarios: Aquellos que de una forma u otra son suministrados al algoritmo para su procesamiento. Van a ser los encargados de brindarle la información del exterior que necesita la máquina para dar solución al problema planteado. Dichos datos pueden ser entrados de formas muy variadas, la más usual es que sean introducidos por el hombre o pueden ser tomados directamente por la computadora desde algún dispositivo autómata conectado a ella.

  • Temporales: Datos auxiliares que se necesitan para completar la tarea a realizar. La mayoría de las veces van a ser útiles sólo para la propia computadora y van a contener información que no se conocía al inicio del programa pero que tampoco constituye el resultado que se pide. Son usados algunas veces para simplificar cálculos matemáticos complejos, aunque también algunos programadores los usan con el objetivo de hacer más claras y legibles las operaciones ejecutadas dentro del algoritmo.

  • Finales: Son aquellos que representan el resultado del procesamiento de la información. Siempre van a ser calculados o elaborados por la computadora y van a tener la función de mostrar al exterior el resultado para lo cual se confeccionó el programa.

Existen otras características que aunque no son obligatorias nos pueden ayudar a diseñar algoritmos más potentes y eficaces. El uso de estas por parte de los programadores demuestra la experiencia y maestría adquiridas durante el ejercicio de sus labores, a la vez que denotan buenos hábitos de programación. A continuación se mencionan y se les da explicación a un grupo de ellas.

  • Validación: Consiste en chequear el contenido del dato y ver si se ajusta al rango pre-establecido por el programador. Todos los lenguajes de programación tienen sus variados tipos de datos y controlan por ejemplo, que en una variable declarada como cadena de caracteres no se almacene un valor numérico y viceversa. Estos chequeos son ejecutados de forma automática por dichos lenguajes, pero muchas veces necesitamos restringir la validación a un grupo más selecto dentro de un mismo tipo de dato. Supongamos que en nuestro algoritmo queremos pedir el sexo de X cantidad de personas. Todos conocemos que esta información tiene dos estados posibles (Femenino o Masculino) y que no es necesario almacenar la palabra completa sino que bastaría con entrar las iniciales F ó M. En este caso podemos restringir la entrada por parte del usuario a estas dos letras. En el caso que por descuido u otra causa, sea entrada otra letra diferente, entonces mandaríamos un mensaje de error (es opcional) y volveríamos a preguntar por la información. Hoy día los potentes lenguajes visuales y orientados a objetos brindan a sus programadores fuertes herramientas para realizar estos trabajos, ya que la validación nos permite reducir en gran medida la cantidad de errores posibles contenidos en los datos de entrada. Por último cabe añadir que estos chequeos también se aplican a las operaciones y cálculos efectuados durante el proceso algorítmico.

  • Modularidad: No es más que la fragmentación de un algoritmo en varios módulos (algoritmos más pequeños) y que estarán relacionados entre sí. Existe un viejo refrán que dice "Divide y vencerás" y se ajusta perfectamente a lo que tenemos que aplicarle a la estructura de los algoritmos. Hay que reconocer que todas las secuencias de pasos para resolver un problema podemos unirlas en un solo "bloque", logrando un funcionamiento perfecto a la hora de ejecutarlo. También podemos dividir el "bloque" en varios fragmentos (llamados en programación módulos) bien escogidos, relacionarlos unos con otros según convenga y obtener un igual resultado con su ejecución. Esta última forma presenta un número grande de ventajas respecto a la primera, siendo unas de las más importantes la facilidad de su posterior estudio y de hacerle modificaciones al algoritmo. La modularidad se aplica también a la estructura de las Bases de Datos utilizadas por nosotros. Esta característica va a ser un elemento de mucha utilidad dentro del grupo que desarrolla un producto informático, pues nos va a permitir poder dividir el trabajo entre varios programadores. De esta manera se puede lograr una mayor especialización y calidad en el trabajo, ya que los programadores siempre se van a inclinar por algunos aspectos de la programación más que por otros y esto se debe aprovechar para repartir las tareas según las habilidades de cada cual.

  • Descripción: Es todo comentario, aclaración o algún otro tipo de nota que se puede poner en cualquier parte del programa con el fin de explicar algo. Todo algoritmo debe llevar un comentario al inicio donde se detallan, entre otras cosas, nombre dado por quien lo diseñó, fecha de su última actualización, nombre del autor y una breve descripción sobre el problema que resuelve. También, a todo lo largo del algoritmo se le deben poner comentarios a determinadas operaciones o grupos de estas para aumentar su comprensión a la hora del estudio y mantenimiento, máxime si estas tareas serán realizadas por alguna persona que no participó en la confección inicial del algoritmo. También se puede dar el caso, por ejemplo, que hagamos un algoritmo y al cabo de un año se nos presente la tarea de modificarlo. Durante este tiempo es muy probable que se nos olviden muchos análisis hechos al principio. Aquí, en este caso nos pudieran auxiliar muy bien todos los comentarios y textos que escribimos con anterioridad describiendo los pasos dentro del algoritmo. Los lenguajes de programación implementan de una forma u otra la vía por la cual adicionar comentarios dentro del código. Por supuesto, a la hora de ejecutar dicho programa todas las líneas de código son ignoradas. En muchas de las metodologías usadas para servir de guía en el diseño y construcción de sistemas informáticos, es posible encontrar algunos pasos dedicados a la confección de documentaciones que ayudan también al entendimiento del código elaborado.

  • Redundancia: Se le llama a la repetición inútil de una o más operaciones. Esta es una característica negativa que los programadores van eliminando en la misma medida en que van adquiriendo experiencia, conocimientos y habilidades. Estas repeticiones innecesarias no sólo se pueden ver en las operaciones que conforman al programa sino también durante el almacenamiento, procesamiento y presentación de los datos. Se han visto programas que solicitan al usuario una cantidad determinada de información primaria, algunas de las cuales no necesitan ser pedidas y mucho menos guardadas ya que pueden ser calculadas u obtenidas durante la ejecución del algoritmo. Supongamos, para ejemplificar lo anterior, que tenemos almacenado un grupo de datos sobre X cantidad de personas donde se encuentra la fecha de nacimiento de cada una. Sería una redundancia en este caso, guardar también la edad de dichas personas ya que esta puede ser calculada en cualquier momento partiendo de su fecha de nacimiento y la fecha actual. Otros ejemplos de redundancia los podemos encontrar en las presentaciones de los datos, ya sean a través del monitor del ordenador o mediante una impresora. Tomemos como ejemplo el listado de todos los trabajadores de una empresa donde laboran hombres y mujeres, de los cuales se tiene almacenado el nombre, los apellidos y el sexo de cada uno. Vamos a suponer que queremos sacar un listado de todos los hombres con que cuenta dicho centro. Para eso pondríamos en el encabezamiento de la hoja un título que indique el contenido de la relación, el cual podría ser: "RELACION DE TRABAJADORES DE SEXO MASCULINO". Como el título aclara bien que no aparecerán en la lista los nombres de las mujeres, sería redundante también en este caso mostrar el dato del sexo al lado de cada nombre dentro de la relación.

  • Nivel o grado de automatización: El objetivo de todo algoritmo en computación es automatizar una determinada tarea o parte de ella. Cuando nos proponemos diseñar un programa para que sea ejecutado por una computadora debemos tratar de que este realice todas las operaciones posibles. Mientras mayor sea el nivel de automatización, mayor será también la cantidad de usuarios que podrá hacer uso de este programa. Expliquemos esto a través de un ejemplo. Supongamos que somos usuarios de un programa que como dato final nos brinda la producción real de una empresa X y se desea saber si cumplió el plan o incumplió. Solamente los usuarios que conozcan el Plan de Producción de la empresa podrán dar esta respuesta. Sin embargo, si el programa trabajara entre sus datos con el valor de dicho plan, entonces después de una comparación automática este nos podría dar como dato final si la empresa X cumple o incumple, logrando así que cualquier persona que se siente frente a la máquina pueda ver fácilmente el cumplimiento o incumplimiento de dicha empresa. Con el grado de automatización hay que tener en cuenta un aspecto muy importante y que pasa muchas veces inadvertido y es que a mayor cantidad de tareas automatizadas menores serían los conocimientos que necesitaría tener el usuario sobre dichas tareas, ya que con oprimir 2 ó 3 teclas se estarían ejecutando un grupo de operaciones que pasarían transparentes para él. Todo esto hace que cobre una especial importancia la capacitación de los usuarios finales de las computadoras, no sólo desde el punto de vista de las nuevas tecnologías sino también respecto a las operaciones automatizadas que ocurren dentro del ordenador y que de no ser por estos últimos habría que hacerlas de forma manual. Todo esto es muy usual apreciarlo en los sistemas dedicados a automatizar los procesos contables en las empresas, donde los económicos y contadores operan una computadora a través de opciones programadas de antemano y con el tiempo olvidan los asientos contables correspondientes, lo que provoca la incapacidad de estos a la hora de hallar diferencias en la contabilización y realizar los correspondientes ajustes.

  • Generalidad: Esta característica nos permite aumentar el radio de aplicación de un algoritmo así como su potencia. Tenemos, por ejemplo, que es bueno poseer un algoritmo que permita calcular sólo las potencias de 3, pero sería mejor tener uno que calculara las potencias de cualquier número natural que pueda ser entrado. A esta propiedad que presentan los algoritmos de poder trabajar en una cantidad grande de situaciones se le denomina también carácter masivo. Esta característica requiere un gran conocimiento por parte del diseñador de toda el área que se pretenda automatizar. A la hora de generalizar nuestro algoritmo tendremos que tener en cuenta un numeroso grupo de aspectos, de los cuales a continuación, relacionamos y explicamos algunos de ellos:

  • Uso de datos variables: Los datos variables permiten que nuestro algoritmo pueda trabajar con diferentes juegos de valores, aumentando así la flexibilidad de este y su fácil adaptabilidad a otras áreas afines donde se desee ejecutar en el futuro. El diseñador tiene que tener bien presente cuando realiza su trabajo, que la incorporación de datos constantes provoca zonas rígidas e inalterables en el interior del algoritmo, las cuales se van a comportar siempre de la misma forma, independientemente de los valores que puedan tomar los datos variables. Los datos que estén contenidos en el interior de estas zonas solamente podrán ser cambiados a través de una nueva codificación de nuestro algoritmo.

  • Incluir características ausentes: Cuando se desean automatizar las tareas de un área determinada no podemos limitarnos a realizar un estudio de sus particularidades, sino que debemos remitirnos también a estudiar otras áreas afines. Todo esto con el objetivo de poder incluir en nuestro programa la mayor cantidad de elementos posibles, incluso aunque estos no aparezcan en nuestra área, creando así un camino mucho más fácil para una futura generalización del programa.

  • Optimización: A un mismo problema podemos casi siempre darle algorítmicamente, más de una solución. Es por eso que después de tener elaborado un programa, nunca debemos conformarnos con su construcción, sino que debemos estudiarlo y hallar otras vías de confección más óptimas. A medida que pasa el tiempo, el programador va adquiriendo mayor dominio técnico de su especialidad, por eso es que cuando hayan pasado 1 ó 2 años de haber creado un algoritmo, dicho programador estará en condiciones de volver a confeccionarlo, pero esta vez dándole una mayor optimización. El análisis y optimización de algoritmos ya creados con anterioridad es una de las vías prácticas de aprendizaje más recomendada a la vez que constituye un método de auto superación idóneo. Dentro de las características que normalmente se tratan de optimizar existen tres a las cuales los programadores le prestan mucha atención, ellas son:

  • Rapidez de ejecución de las operaciones.

  • Tamaño del algoritmo.

  • Cantidad de datos variables y constantes a utilizar.

Esta característica es la que dio origen a la famosa palabra HACKER, actualmente utilizada incorrectamente para llamar a todos aquellos intrusos y piratas de las redes informáticas. Todo comenzó a finales de los años 50 y principio de los 60 donde un grupo de programadores se trazó la meta de resolver problemas pero utilizando la menor cantidad posible de líneas de código. A todas las soluciones ingeniosas que ahorraban líneas o permitían que algo se hiciera más rápido le empezaron a llamar "hacks" (significa algo así como un golpe hábil dado con un hacha) y de ahí que todo aquel que demostrara su habilidad en esto se le empezara a llamar "hacker".

En algunas literaturas como por ejemplo ESTRUCTURA DE DATOS EN PASCAL de los autores Aaron M. Tenenbaum y Moshe J. Augenstein es posible encontrar criterios tales como "Un programador debe siempre preocuparse por la facilidad de lectura del código que ha generado.". Este es un aspecto que resulta un tanto relativo en cuanto a lo que cada diseñador, analista o programador según su nivel y experiencia alcanzado, entienda por "facilidad de lectura". Cuando un especialista en programación de máquinas computadoras, con bastante experiencia en su labor, confecciona un algoritmo, el mismo podría ser muy difícil de entender por parte de aquellos que dan sus primeros pasos por el mundo de la informática. No obstante, tanto el que lo diseñó, como otros con suma experiencia también, lo podrían entender perfectamente, a la vez que lo pudieran encontrar incluso sencillo.

El algoritmo como sistema informático

El objetivo de todos los algoritmos confeccionados va a ser casi siempre su preparación y posterior ejecución en una computadora. Estos se van a unir y constituir lo que se puede llamar un sistema automatizado.

En la actualidad existen numerosas metodologías para el análisis y diseño de sistemas automatizados, los criterios de cuales son las mejores o las peores discrepan mucho entre aquellos que hacen uso de ellas ya que cada cual defiende la suya (no obstante se pueden ver muchas cosas en común entre todas ellas, incluso algunas se elaboran a partir de otras ya existentes). Lo que si se hace necesario es guiarse por una metodología determinada a la hora de llevar a cabo cualquier proyecto por muy pequeño que este pueda ser. A estas metodologías también se les suele conocer como ciclo de vida del sistema.

En la presente obra explicaremos los pasos o etapas de una pequeña y sencilla metodología que permitirá al lector contar con una herramienta capaz de ayudarlo a resolver problemas usando algoritmos, así como también le servirá de base para estudios posteriores de otras metodologías mucho más complejas. Este tema debe ser muy bien estudiado y profundizado por todos aquellos que pretendan dirigir a un grupo de especialistas en la construcción de sistemas automatizados, tarea donde no se necesitan grandes conocimientos de programación pero sí son imprescindibles técnicas organizativas, metodológicas y de dirección.

A continuación se describen los pasos para resolver cualquier problema con el auxilio de una computadora:

  • 1- Análisis y descripción matemática del problema: Lo primero que debemos hacer es determinar los intereses de automatización que desea el usuario, así como las condiciones existentes donde se desarrolla el trabajo actual. Hecho esto se procede al importante paso del entendimiento de forma correcta del problema planteado. Está de más decir lo obvio que resulta el no poder dar solución a ningún problema sin haberlo entendido previamente.

Después de la lectura y entendimiento del problema pasamos a extraer todos los datos con los cuales disponemos, ya sean primarios, temporales o finales. Muchas veces para lograr esto es necesario profundizar en libros, consultas con especialistas en ese tema u otra vía que sea aplicable, todo esto en dependencia del tipo de problema y su nivel de complejidad. Esto se hace con los objetivos siguientes:

  • Agrupar los datos para un mejor análisis del algoritmo.

  • Conocer si con los datos primarios que poseemos, es posible llegar a los datos finales (o sea comprobar que el problema tiene solución a través de un algoritmo).

  • Hacer un eficiente diseño de entradas de datos.

  • Hacer un eficiente diseño de salidas de datos.

Durante este paso encontraremos el problema matemático que describe el problema actual. Una vez analizado el problema a fondo debemos encontrar las formas y vías generales para resolverlo, estudiando las variantes de solución más factibles, eficientes y óptimas. Algunas veces nos vamos a encontrar en la situación de que se puede derivar la proyección de varios sistemas. Si se nos diera este caso entonces cada uno de ellos recorrerá a partir de ahora (y de forma independiente) los pasos que siguen.

Al finalizar se elaborará una propuesta general de automatización donde se incorporará un estudio de factibilidad del proyecto (el cual deberá estar muy bien fundamentado técnica y económicamente) y se presentará para ser aprobada por el usuario.

  • 2- Elaboración del esquema lógico: Aquí se encontrarán los métodos algorítmicos que nos permitan hallar la solución al problema matemático elaborado en el paso anterior. Esto no es más que esquematizar el procedimiento lógico a seguir, tomando en consideración las características y posibilidades de trabajo de la computadora. En este paso entra a jugar un papel muy importante los conocimientos adquiridos por la persona encargada de confeccionar el algoritmo. En la confección de muchos sistemas informáticos pequeños es común encontrarnos con personas que omiten por completo este paso para realizar la tarea directamente dentro de la computadora.

  • 3- Convertir el algoritmo en programa: Después de esquematizado el procedimiento mediante un algoritmo, estamos en disposición de hacerlo entendible por la computadora. Para ello es necesario traducir toda la secuencia elaborada en un lenguaje comprensible por esta última. Para lograr esta traducción es necesario disponer de un programa compilador. Hoy en día existe gran variedad de estos programas, pudiéndose hacer mención aquí de los más usados, que son: C++, Turbo Pascal y BASIC. También contamos con sus versiones más modernas desarrolladas para trabajar en un ambiente gráfico visual, entre ellas tenemos Visual Basic, Borland Delphi, etc.

  • 4- Ejecución y prueba del programa: Después de finalizado el paso anterior ya estamos listos para ejecutar nuestro programa en una computadora. Durante este proceso se pueden detectar algunos errores, los que deben ser corregidos antes de continuar hacia el paso siguiente. Es usual encontrar durante el diseño de algunos programas como muchos diseñadores y programadores tienden a confundir este paso con el que sigue, llegando incluso a unirlos y aceptarlos como uno solo.

  • 5- Análisis y comprobación de los resultados: Cuando han finalizado los arreglos y ajustes al código del programa, pasamos entonces a comprobar la veracidad de los datos que nos brinda como resultado. Las primeras pruebas que se deben hacer se realizan con datos primarios cuyo resultado final es conocido de antemano. Esto se hace con el objetivo de comprobarlos y ver si ambos son iguales. Hecho esto se pasa a realizar algunas pruebas con datos reales y dentro de un entorno idéntico a donde va a ser explotado.

En algunas metodologías es posible encontrar este paso dividido en varias etapas de pruebas.

Una forma recomendable para hacer pruebas reales con el sistema automatizado es llevar, durante algún tiempo y de forma paralela, el uso de este junto con el sistema manual antiguo.

  • 6- Explotación del programa: En este paso se ejecutarán todos los cambios necesarios en el área a automatizar. Debemos tener en cuenta que muchas veces estos cambios se expanden a otras áreas, que aunque no se adentran directamente en la automatización, influyen de una forma u otra en el problema que ha sido objeto de estudio.

Es en este paso donde el programa le es útil al hombre. Aquí es donde se va a producir el intercambio de datos reales así como su procesamiento.

No se puede descartar la aparición de errores en este paso, la experiencia nos dice que la gran mayoría de los programas una vez que son puestos en explotación y aunque hayan pasado todas las etapas de prueba, presentan algún que otro problemita. Todas las eventualidades ocurridas en este paso deben ser documentadas y archivadas para que se puedan usar como material de trabajo en el paso siguiente.

  • 7- Mantenimiento del programa: Este paso se nutre de la experiencia acumulada durante el tiempo de explotación del programa. Aquí se le hacen modificaciones al código del programa o a la estructura de las bases de datos teniendo en cuenta algunos aspectos fundamentales que son:

  • Errores ocurridos y que no fueron detectados durante las pruebas.

  • Adiciones de opciones nuevas al programa.

  • Cambios ocurridos en el área automatizada y que generen modificaciones al programa.

  • Modificaciones internas del código del programa producto de alguna optimización.

Hay metodologías que incluso clasifican los mantenimientos teniendo en cuenta los tipos de modificaciones que realizan estos a los sistemas, bases de datos o a la propia lógica y filosofía del proyecto. De todas formas cabe decir que se tiene que tener mucho cuidado a la hora de hacer arreglos y adiciones a los programas en esta etapa, pues se corre el riesgo de crear errores o problemas en partes donde antes funcionaban de lo más bien.

Todo aquel, que en algún momento de su vida ha estado vinculado de una forma u otra al trabajo de implantación de sistemas automatizados, podrá darse cuenta de que al finalizar el paso número 7 no ha concluido su tarea, sino que, a partir de aquí se establece una repetición necesaria de los últimos dos pasos. Entonces es cuando nuestro sistema comienza a crecer y a desarrollarse nutriéndose con la experiencia de la práctica diaria, adquiriendo de esta forma mayor fuerza y realismo.

En algunas metodologías podremos encontrar un paso independiente dedicado a la confección de la documentación del proyecto. En realidad en cada etapa o paso se deberá generar una información propia (que no entraremos a detallar aquí). Toda esta documentación que se va generando a lo largo de la ejecución del programa se va recopilando y pasará a formar parte del llamado Expediente del proyecto.

Toda la documentación que se genera se va a dividir de forma general en dos grandes grupos, que son:

  • Orientada al proyecto: Es creada en el primer paso y desarrollada en cada uno de los siguientes. Va a contener información interna del sistema así como la descripción del desarrollo del trabajo en general.

  • Orientada al usuario: Es confeccionada en el paso 3 y puede sufrir modificaciones en los que le siguen. Debe contener toda la descripción de como trabajar con el sistema incluyendo su instalación y explotación.

El grupo de trabajo puede incluso, teniendo en cuenta la complejidad del sistema en estudio, omitir algunos pasos durante su desarrollo.

Hoy en día se hace necesario que el período de confección de un programa (ciclo de vida) sea lo más corto posible. El acelerado desarrollo de las técnicas informáticas nos obliga prácticamente a no demorar mucho el tiempo invertido en cada fase del ciclo de vida de nuestro algoritmo, ya que lo que hoy es nuevo y novedoso, mañana puede ser viejo, e incluso obsoleto. Una muestra de esto lo constituyen los nuevos lenguajes de programación visuales orientados a objetos, también llamados RAD (Diseño Rápido de Aplicaciones). En ellos podemos ver mecanismos y herramientas que nos permiten realizar, de forma muy rápida, tareas que en los lenguajes más antiguos nos tomarían mucho más tiempo.

Análisis del costo-beneficio de un sistema informático

Las personas o entidades que se dedican al diseño y desarrollo de sistemas informáticos necesitarán cobrar por esta tarea ya que en realidad lo que están haciendo es vendiendo un producto. El cobro que se efectúe estará en dependencia del costo del proyecto en general y lo podremos dividir en:

  • Costo Directo: Aquí se tendrá en cuenta el costo de la fuerza de trabajo, el consumo que se haya hecho de materiales y el costo de uso de los medios técnicos empleados.

  • Costo Indirecto: Se contemplará aquí todos aquellos costos que influyen de una manera u otra sobre el desarrollo de la confección del software pero que no lo hacen de forma directa.

Al implementar cualquier proyecto nos vamos a poder encontrar con los beneficios que este reporta, estos podrán ser clasificados de la forma siguiente:

  • Tangibles: Son aquellos que pueden ser calculados, por ejemplo:

  • Reducción del personal que realizaba la tarea.

  • Disminución de todo tipo de errores imputables a los seres humanos.

  • Rapidez en el procesamiento y presentación de la información.

  • Disminución de algunos gastos.

  • Intangibles: Son aquellos que resultan difíciles de calcular, entre ellos tenemos:

  • Humanización del trabajo.

  • Rapidez en el análisis de los resultados.

Diagramas de bloques

Los diagramas de bloques (también llamados Diagramas de flujos) son lenguajes de descripción de algoritmos. Estos son reconocidos en muchas literaturas como los más universales de su tipo, debiéndose este éxito a la reducción en gran medida del uso del lenguaje natural para describir los pasos a seguir.

Las operaciones a realizar van a estar representadas por símbolos, los cuales van a contener en su interior los datos utilizados en dichas operaciones así como las variables que se van a usar. En este capítulo se va a explicar detalladamente el diseño y confección de algoritmos mediante el empleo de los diagramas de bloques. Se le recomienda al lector que repase y estudie cuidadosamente cada página de este capítulo y no pase a la siguiente hasta tanto no se comprenda el contenido de la anterior, ya que cada lección nueva lleva implícita la comprensión de las lecciones anteriores a ella.

Comencemos a dar los primeros pasos utilizando un pequeño y sencillo problema planteado a continuación:

1- Se desea conocer el resultado de la suma de dos números entrados al programa.

Después de leer el problema 1 pasamos a extraer y agrupar los datos que utilizaremos, esto nos quedaría más o menos así:

Datos primarios (de entrada) Datos finales (de salida)

  • A- Primer número S( A+B

  • B- Segundo número

Como podemos ver se pide un solo dato como salida y para su obtención contamos con valores existentes, por lo que llegamos a la conclusión de que dicho problema es algorítmicamente soluble. En este ejemplo no hace falta el uso de datos temporales.

Pasemos ahora a la construcción del diagrama. Todo algoritmo debe llevar algo que indique donde empieza y donde termina, en los diagramas de bloques esta función la va a asumir el símbolo siguiente:

edu.red

En su interior podrá llevar sólo un texto de dos posibles. Este texto será "Inicio", para indicar por donde se tiene que empezar a ejecutar el algoritmo, o "Fin" que nos dirá donde termina la ejecución del mismo. Conociendo lo anterior podemos empezar nuestro algoritmo como sigue:

edu.red La línea terminada en flecha nos indica la dirección

a seguir en la ejecución de los pasos del diagrama.

Para poder darle la solución al ejercicio 1 necesitamos introducir en la computadora los datos que nos permitan ejecutar las operaciones, los símbolos usados para la lectura de datos desde el exterior pueden ser de dos formas diferentes las cuales se muestran a continuación:

edu.red

Nosotros en el transcurso del libro trabajaremos con el de la izquierda por ser este el más ampliamente usado. En el interior de dichos símbolos podremos poner el identificador del dato que vamos a leer, pudiéndose poner también más de uno, cada cual separado por una coma. En el ejercicio 1 tenemos que leer dos valores numéricos, completando nuestro diagrama con lo hasta ahora aprendido pudiéramos hacerlo de cualquiera de las formas representadas en el esquema 3.1.

edu.red

Sólo nos falta realizar la suma y mostrar el resultado para acabar de resolver el problema. La ejecución de operaciones aritméticas y asignaciones se representan con el símbolo:

edu.red

Dentro de este símbolo podemos poner una o más asignaciones, según nuestra conveniencia. Poniendo como ejemplo la suma del ejercicio 1 tendríamos:

edu.red

Cabe hacer notar que en los diagramas de bloques la asignación de un determinado valor a cualquier variable se representa con una flecha, la cual va a ir de la operación o valor a la variable, siempre de derecha a izquierda por lo tanto es una regla aquí que el nombre o identificador de dicha variable se encuentre siempre a la izquierda de la asignación.

Para poder conocer el resultado producido por nuestro programa necesitamos decirle a la computadora que muestre a este ya sea a través de un monitor o una impresora que son los periféricos de salida más comunes. Esta orden de salida se representa con el símbolo:

edu.red

Dentro de este podemos imprimir más de un resultado. Con lo visto hasta ahora podemos concluir la confección del algoritmo para el ejercicio 1, el cual se muestra, completo en el esquema 3.2.

edu.red

A la hora de confeccionar un programa en cualquier lenguaje, ya sea Pascal, C++, etcétera, partiendo de este algoritmo y ejecutándolo en una computadora, veríamos como nos pide la entrada de dos valores numéricos y después nos mostraría un valor que sería la suma de dichos números entrados anteriormente por nosotros.

Cuando estamos enfrascados en la construcción de algoritmos hay que tener en cuenta el evitar la confección de posibles partes ilógicas. Veamos esto a través de los dos ejemplos mostrados en el esquema 3.3.

edu.red

En el ejemplo del esquema 3.3.a se realiza una operación de resta de dos valores (A y B) antes de que estos sean conocidos por la computadora (como se aprecia la instrucción de lectura aparece aquí después de la de resta). En el otro ejemplo, el del esquema 3.3.b, se pueden ver dos asignaciones de un valor a una misma variable, esto funcionalmente no está mal, pero la asignación tiene un carácter destructivo (o sea cuando a una variable cualquiera se le asigna cualquier valor se destruye el que esta tenía) y en la instrucción que seguiría en este ejemplo se trabajaría la "F" con valor 0 y se perdería el resultado de la suma. En el esquema 3.4 se reproduce una variante correcta de cada uno de los ejemplos anteriores.

edu.red

Pasemos ahora a resolver otro problema muy parecido al anterior, dice así:

2- Dadas las tres notas de un estudiante (Matemática, Física y Biología), se desea conocer el promedio obtenido.

La agrupación de los datos nos quedaría como:

edu.red

En el esquema 3.5.a podemos apreciar el diagrama de bloques correspondiente a este ejercicio. Debido a la sencillez de este problema, podríamos darle también otras soluciones en las cuales no usaríamos datos temporales sino solamente un dato final e incluso prescindir de este último. Para la variante que se representa en el esquema 3.5.b se partió de la agrupación de datos siguiente:

edu.red

edu.red

La otra solución posible representada en 3.5.c no hace uso más que de datos primarios, ya que el resultado se imprime directamente.

Razonamiento Automatizado. Condiciones.

En computación podemos encontrar el razonamiento en dos formas diferentes. Una, contenido en el interior de las bases de datos (dinámico) y otra en el interior de nuestros programas (estático). En este libro nos dedicaremos solamente a estudiar la forma de programar el razonamiento desde el interior del algoritmo.

Los programas se proveen normalmente de datos primarios simples, dichos datos pueden ser suministrados directamente por el operador de la computadora (llamada entrada interactiva), pueden ser recibidos de algún equipo periférico que se encuentre conectado al ordenador (medidores de temperatura, cámaras de televisión, sensores, robots, etc.) o pueden ser obtenidos por el programa desde el interior de una o varias bases de datos.

Estos datos simples van a suministrar a su vez una información simple o atómica con la cual el programa tendrá que trabajar para poder ofrecer una respuesta procesada y acorde a las necesidades para las cuales fue diseñado y construido. Como ejemplos de información atómica podemos mencionar los siguientes:

  • A- Raúl es un hombre.

  • B- Juana es una mujer.

  • C- Raúl es joven.

Podemos apreciar que los datos A, B y C nos dan elementos bien sencillos (también llamados proposiciones simples) de un determinado universo, en los cuales no se aprecia razonamiento alguno, así como tampoco información procesada. El razonamiento general se representa en el interior de los algoritmos como un conjunto de reglas cuya forma elemental es:

Si C, entonces A

Donde C es alguna proposición simple que de cumplirse podrá dotar a nuestro algoritmo del conocimiento A. Veamos esto a través de un ejemplo, supongamos que estamos trabajando con personas y tenemos lo siguiente:

Si persona es hombre, entonces persona no es mujer

Con esta proposición más compleja la computadora podrá "razonar" que toda persona es hombre o mujer y que no podrá existir ninguna persona con las dos características a la vez.

Existen algunos operadores (llamados operadores lógicos) que nos permiten a la hora de escribir proposiciones, aumentar el nivel de complejidad de estas. Por ejemplo:

Si B y C, entonces A

Aquí podemos apreciar como se tienen que cumplir las proposiciones B y C para que pueda existir el conocimiento A. Cuando en cualquier proposición compleja nos encontramos el operador lógico "y" (en muchas literaturas sobre este tema se le hace mención por su equivalente en inglés AND aunque dichos textos estén escritos en español) entre dos o más proposiciones, siempre será necesario que todas se cumplan para poder hacer válido el conocimiento A.

Otro operador lógico lo constituye el "o" (también usado con su traducción al inglés como OR), con la diferencia de que cuando se encuentra entre dos o más proposiciones, sólo bastaría con que se cumpliera una sola para que se haga válido el conocimiento. Es decir:

Si B o C o D, entonces A

Dado este razonamiento con el operador "o", se puede dar el caso de que una sola proposición se cumpla, ya sea B o bien puede ser alguna de las otras, en dicho caso se aplicaría el conocimiento A. Para que no pueda aplicarse A tendrían que no cumplirse ninguna de las tres proposiciones ya que si se cumplieran las tres proposiciones también se aplicaría el conocimiento A.

En los textos de matemática que aborden el tema de la lógica, así como en otros de computación que profundicen más en cuanto a operadores lógicos, el lector interesado podrá encontrar mucha más información. También conocerá otros operadores de la lógica que no se han tratado aquí por no ser objetivo del libro.

Esta forma de representar conocimiento también es usada dentro de los algoritmos para dada una determinada condición, se ejecute una tarea específica. La sintaxis sería la misma, o sea:

Si C, entonces A

Donde C seguiría siendo una proposición, o grupo de ellas separadas por operadores lógicos y A una instrucción (o grupo de ellas) a ejecutar. Expliquemos un poco más esto a través de un ejemplo. Supongamos que se nos plantea el siguiente problema:

3- Conocida la nota de matemática de un alumno, decir si está aprobado o suspenso (el mínimo de aprobado es 60 puntos).

La línea de algoritmo que haría esta operación sería:

Si (Nota>60) o (Nota=60) entonces Decir "Está aprobado"

Como puede observarse, el algoritmo nos diría "Está aprobado" en el caso de que la nota sea mayor o igual que 60, pero en caso contrario, que la nota fuera menor que 60, entonces no nos brindaría ninguna información. Para hacer un uso más eficiente de la instrucción Sientonces… podemos usarla en una forma más compleja:

Si C entonces A sino B

Vemos ahora que si se cumple la proposición C entonces se ejecutaría A, pero en caso de que C no se cumpla entonces se ejecutaría B. Después de ver esto podemos remitirnos al ejercicio 3 cuya línea de código nos quedaría de forma más completa de la siguiente forma:

Si (Nota>60) o (Nota=60) entonces Decir "Está aprobado"

sino Decir "Está suspenso"

Como se planteó anteriormente en el capítulo I, podemos llegar a la solución de un problema por diferentes vías y métodos. Dejamos las dos siguientes soluciones para que el lector las analice y compruebe que devuelven el mismo resultado que la que hemos planteado como ejemplo en el ejercicio 3. El primer caso asume las notas como un valor numérico entero solamente, o sea sin parte fraccionaria, de lo contrario no serviría.

Si Nota>59 entonces Decir "Está aprobado"

sino Decir "Está suspenso"

Si Nota<60 entonces Decir "Está suspenso"

sino Decir "Está aprobado"

La instrucción Si…entonces…sino también tiene un símbolo (llamado símbolo de decisión, estructura condicional o alternativa simple) que la representa dentro de los diagramas de bloques de la siguiente manera:

edu.red

Dentro del símbolo se coloca la condición a cumplir. Las flechas que salen indican los caminos a seguir según sea evaluada dicha condición, si se evalúa como verdadera entonces, seguiríamos por la flecha SI y en caso contrario, o sea, que sea evaluada como falsa seguiríamos por la flecha NO. En este símbolo la posición de las flechas no es obligatoriamente de la forma que se muestra, por lo que se podrá poner la flecha SI saliendo para el lado o para abajo según convenga.

Nos encontramos ahora en disposición de darle solución al problema 3 mediante un algoritmo de diagramas de bloques. Este se muestra en el esquema 3.6.

En el proceso algorítmico las condiciones se determinan, en general, partiendo de la comparación entre datos, los cuales deben haber tomado valores antes de efectuarse la misma. Debemos destacar que una vez que estemos situados en un símbolo de decisión podremos escoger un solo camino a la vez.

edu.red

Variables indicadoras o "Banderas".

Las "banderas" (conocidas también por su significado en inglés como flags) son datos variables cuyo valor no lo va a constituir el resultado de una operación aritmética, sino que va a ser puesto automáticamente en determinados lugares del algoritmo con el fin de indicar algo. Estas variables indicadoras van a tener dos funciones muy importantes, que son: servir de información al usuario en forma de dato final y/o ser utilizadas en otras partes dentro del algoritmo con el propósito de según su valor tomar determinadas decisiones.

Hay que saber distinguir entre un dato variable normal y una "bandera". Las variables normales se usan para trabajar con su valor y usarlo en operaciones matemáticas, para imprimir textos, etc. Las variables indicadoras se van a usar para analizar su valor, el cual puede variar en cualquier paso del algoritmo. Son muy útiles para conocer las partes que fueron ejecutadas dentro de un algoritmo ramificado. En algunas literaturas a estas variables se les reconoce como Interruptores o switches.

Contadores y acumuladores.

Un contador no va a ser más que una variable cuyo valor se va incrementar o decrementar en una cantidad fija.

En el trabajo con las iteraciones o ciclos es muy común (y en algunos casos es hasta obligado) la necesidad de efectuar conteos en el interior de los mismos. Podemos citar como ejemplo el caso del ciclo por conteo donde la instrucción que efectúa la cuenta forma parte incluso de la sintaxis del bloque. Esto se debe a que la culminación del ciclo va a estar en estrecha dependencia con el valor que obtenga la variable contenida en el interior del contador.

Por otro lado tenemos que el acumulador (también llamado sumador) va a ser una variable cuyo valor se va incrementar varias veces pero en cantidades variables. Va a ser preciso que se inicialice con el valor 0 ya que va a ser utilizado en los casos en que se desee obtener el total acumulado de un determinado conjunto de cantidades.

Iteraciones.

Es muy común a la hora de diseñar un algoritmo encontrarnos con la necesidad de repetir un grupo específico de pasos durante una determinada cantidad de veces. A esta parte del proceso es a lo que se llama iteración o ciclo (también es conocido por muchos como bucle o lazo).

La iteración la vamos a poder encontrar en la inmensa mayoría de los algoritmos, ya sean estos más simples o más complejos. Esto nos da una muestra de lo importante que se hace su estudio y aprendizaje, ya que el dominio de esta técnica de programación dota a los algoritmos de robustez y eficiencia. El no hacer uso de los ciclos podría convertir la tarea de diseño de algoritmos en algo verdaderamente tedioso, así como que aumentaría considerablemente el tamaño de nuestros programas.

Existen diferentes tipos de ciclos, pero todos tienen en común un mismo aspecto y es que la cantidad de veces que se repite va a estar controlada por una condición. Este detalle es ignorado muchas veces por los desarrolladores de aplicaciones que trabajan a un alto nivel de abstracción, ya que estos lenguajes proporcionan instrucciones que realizan la tarea de forma transparente al programador.

Atendiendo a si se conoce o no la cantidad de veces que va a ser ejecutado un ciclo, este se puede clasificar en:

  • Ciclo por condición: Es aquel que se caracteriza por presentar la particularidad de que nunca se sabe con antelación cuantas veces se va a ejecutar. Van a existir 2 tipos de ciclos por condición y su principal diferencia va a estar dada por el lugar donde será ubicada dicha condición.

  • Ciclo por conteo: Es usado cuando se conoce de antemano la cantidad de veces que se desea repetir determinado grupo de operaciones. Es mucho más sencillo que todos los demás y es el más utilizado dentro del análisis y solución de problemas. Cuando se imparten clases de programación directamente con un lenguaje de programación, este ciclo es el primero que se estudia debido a su fácil comprensión comparado con el de condición.

En los diagramas de bloques el esquema básico del ciclo por conteo es como se representa en el fragmento de algoritmo mostrado en el esquema 3.7.

Como se puede apreciar este tipo de ciclo hace uso de una variable para controlar la cantidad de veces que es ejecutado, a dicha variable se le llama variable controladora de ciclo y en este esquema de ejemplo se identifica con la letra I.

El primer paso que se realiza es inicializar esta variable controladora con el valor 0. Se puede observar que esta operación, aunque es parte fundamental del ciclo, queda fuera de él y no se repite. Esto se hace con el objetivo de que se comience el conteo de repetición con el número 1, de lo contrario, si no hacemos la inicialización de la variable, la computadora le daría cualquier valor a la hora de usarla en la instrucción siguiente. En el paso siguiente se incrementa en uno el valor de I. Esta instrucción (explicada ya anteriormente y conocida con el nombre de contador) es la que controla la ejecución del ciclo, permitiendo así contar cada repetición que se haga. Después de esto y como tercer paso vienen las operaciones que se desean repetir.

edu.red

El último paso consiste en la condición. Aquí se compara el valor de la variable controladora (en este caso I) con la cantidad total de repeticiones deseadas (CANT), si son iguales, se termina dicho ciclo y se continúa con la ejecución del programa, sino (en el caso de que I sea menor que CANT) entonces se siguen repitiendo las instrucciones en el interior del ciclo.

Cuando se ha adquirido experiencia con el trabajo de análisis y diseño de algoritmos vamos a ver como podemos entonces hacer pequeñas modificaciones a esta estructura. Se van a presentar casos en que por ejemplo el contador será necesario ubicarlo después de las operaciones a repetir y no antes, véase el esquema 3.8.

En numerosos casos podremos aprovechar el valor de la variable controladora de ciclo para usarlo en nuestras operaciones y cálculos desarrollados en el interior del ciclo, también nos encontraremos con algunos ciclos donde la variable es inicializada con un valor distinto de cero. Así podremos ver también otros casos donde se podrá incrementar a la variable controladora de ciclo en más de 1 y en el contador en vez de sumarle 1, le sumaríamos 2, 3, 4, 5…, en dependencia de lo que necesitemos usar según las exigencias del problema planteado.

edu.red

Todas las modificaciones que se hagan dependerán de las exigencias que tenga el problema a resolver. Hay una forma de usar este tipo de ciclo donde, en vez de incrementar a la variable controladora, lo que se hace es decrementarla. Esto se hace muchas veces con el propósito de usar el valor de dicha variable como dato en las operaciones a repetir. Para poder hacer esto tendríamos que cambiar la inicialización de la variable controladora en el primer paso y en vez de asignarle el valor 0 le pondríamos el total de repeticiones (en este ejemplo está representada como CANT), y el contador nos quedaría como:

edu.red

Al final, donde se ejecuta la condición, en vez de comparar a la variable I con CANT, la compararíamos con el valor 0. Aquí podemos destacar una característica muy importante del ciclo por conteo y es que siempre se va a ejecutar completo, ya que no posee ninguna condición que afecte su funcionamiento. Esto lo va a diferenciar mucho de los restantes ciclos existentes.

? En los lenguajes de programación la instrucción del ciclo por conteo es una de las más conocidas y aunque sufre ligeros cambios de sintaxis de un lenguaje a otro, siempre es fácil de identificar, ya que casi siempre va a comenzar con la palabra reservada for. En Turbo Pascal la podemos encontrar de estas dos formas:

edu.red

En el ejemplo 1 vemos el uso normal del ciclo por conteo mientras que en el ejemplo 2 vamos a ver la forma que toma el ciclo cuando en vez de incrementar a la variable controladora lo que hace es decrementarla. Cabe hacer notar que cuando diseñamos este tipo de ciclo con algunos lenguajes de programación, no hace falta que nosotros mismos incrementemos o decrementemos a la variable controladora, ya que la instrucción for que nos brindan dichos lenguajes se encarga de esto de forma automática. En otros lenguajes como el C++ sí se hace necesario que nos hagamos cargo nosotros de alterar el valor de la variable controladora de ciclo ya sea para incrementarla o decrementarla. El mismo ejemplo 1, pero en C++, nos quedaría así:

Ejemplo 1 (C++)

for (i=1; i<=200; i++)

printf("%d", i);

Hay ejemplos de lenguajes de programación donde este tipo de ciclo no comienza con la palabra for. Uno de ellos es el FORTRAN, antiguo lenguaje como ya se explicó en el capítulo I, donde podremos identificar a este ciclo con la palabra DO. A continuación retomamos de nuevo el ejemplo 1 pero esta vez programado en FORTRAN:

Ejemplo 1 (FORTRAN)

DO 32 I=1, 200

  • 32 WRITE (06, 3) I

Es bueno destacar que algunos lenguajes de programación presentan instrucciones que sí permiten interrumpir la ejecución de este tipo de ciclo.

El primer ciclo por condición que vamos a ver es aquel que posee dicha condición al final de su sintaxis. Su diagrama básico se representa en el esquema 3.9.

Este ciclo va a tener la particularidad de que siempre se va a ejecutar aunque sea una vez, debido a que primero procesa las operaciones y después "pregunta". En el siguiente ejemplo veremos como las instrucciones en el interior del ciclo se van a repetir hasta que el valor entrado (y almacenado en la variable Z) sea igual a 0. Aquí también podremos hacer ligeros cambios en cuanto a la obtención del valor de Z, según sean las características del problema planteado.

La variable controladora de ciclo lo constituye en este ejemplo la Z, ella es la que nos va a permitir terminar o no la repetición de las instrucciones. La forma de darle valor a Z en este caso es a través de la entrada por teclado, pero esto puede variar y, por ejemplo, sustituir la lectura por una operación aritmética u otra. Hay que tener mucho cuidado a la hora de definir la forma en que va a tomar valores la variable controladora de ciclo ya que podemos crear un lazo infinito en su ejecución.

edu.red

El segundo ciclo por condición que vamos a estudiar es aquel que posee dicha condición al inicio de su sintaxis. Una particularidad que lo distingue de los demás ciclos es que este no siempre se va a ejecutar (esto estará en dependencia del cumplimiento de su condición). El esquema 3.10 nos muestra dos formas diferentes de representarlo.

Como se puede apreciar a simple vista, en el esquema 3.10.a se hace una redundancia en la lectura de la variable Z (que en este caso es la que controla la ejecución o no del ciclo). Esto nos puede parecer innecesario ya que vemos en el ejemplo diseñado en 3.10.b que se puede lograr el mismo resultado de una forma más óptima. Sin embargo, esta última solución solamente se puede aplicar en los algoritmos de diagramas de bloques debido a que su estructura lo permite y tiene una lógica razonable desde el punto de vista de la programación.

La solución dada en 3.10.a se estudia porque es la forma más cercana a como nos quedaría la estructura de este ciclo por condición si la confeccionáramos en un lenguaje de programación.

edu.red

Variables con subíndices.

Mientras los datos primarios entrados a nuestros algoritmos no sean numerosos podremos declarar diferentes variables para almacenar cada uno de ellos. Pero, ¿qué pasaría si fuéramos a trabajar con volúmenes grandes de estos datos?

Para trabajar con listas grandes de datos de un mismo tipo tendríamos que usar el tipo de dato llamado arreglo (en inglés: array), el cual permite almacenar una colección de elementos del mismo tipo, bajo un mismo identificador, y que podemos tratar dicha colección como un todo, así como también acceder a cada uno de sus elementos por separado.

Cada uno de los elementos de un arreglo se diferencia de los restantes por la posición que va a ocupar dentro del mismo, la cual se va a expresar a través de uno o más subíndices.

edu.red

Los subíndices podrán estar constituidos por una constante o una variable numérica, por ejemplo:

A[2] B[x] A[15]

También podremos usar expresiones más o menos complejas dentro del subíndice a la hora de identificar un elemento dentro de un arreglo, por ejemplo:

Lista[x+3] A[x*y]

Subprogramas o módulos.

Hasta ahora hemos podido ver como todas las instrucciones de cada uno de los algoritmos representados se han encontrado siempre agrupadas en un mismo diagrama de bloques. Esto se debe a que la complejidad de los mismos no ha requerido el empleo de otras técnicas de diseño por parte del programador. Sin embargo, cuando nos enfrentemos a la resolución de un problema de índole mayor, vamos a vernos en la necesidad de emplear una técnica de programación conocida como modularidad.

La modularidad aplicada a la programación de máquinas computadoras consiste en fragmentar el programa principal en "pedazos" más pequeños e independientes llamados módulos o subprogramas (en algunas literaturas se les puede encontrar también con el nombre de subrutinas). Cada uno de los subprogramas va a estar constituido por un grupo de instrucciones específicas para realizar una tarea determinada y van a ser llamados por el programa cada vez que sean necesarios.

Algunas de las ventajas de la división del algoritmo principal en subprogramas son:

  • Facilitan el trabajo en grupo de los diseñadores y programadores.

  • Mejoran considerablemente la legibilidad del código de los algoritmos.

  • Permiten hacer modificaciones dentro de los algoritmos sin necesidad de que se toquen o afecten todas sus partes.

  • Facilita enormemente la realización de pruebas al algoritmo.

  • Se logra la portabilidad, que no es más que el uso de un mismo subprograma en varios algoritmos principales. Esta portabilidad a su vez va a permitir que un programador pueda hacer uso de un subprograma que ha sido desarrollado por otro programador.

  • En el caso particular de que estos subprogramas sean llamados repetidamente dentro de un algoritmo van a permitir el ahorro de líneas de código.

En algunas literaturas se pueden encontrar a diferentes autores nombrando y explicando una serie de criterios para el buen diseño y construcción de los módulos.

Existen dos tipos de subprogramas:

Funciones: Son subprogramas diseñados para realizar una determinada tarea y que siempre tendrá que devolver un valor como resultado, el cual se entregará a través de su identificador. Aunque nosotros podemos diseñar nuestras propias funciones, vamos a encontrar que los lenguajes de programación cuentan con un grupo de estas previamente implementadas.

Las funciones dentro de los algoritmos son llamadas por su nombre y siempre como parte de una expresión.

Procedimientos: Son subprogramas diseñados solamente con el objetivo de realizar una determinada tarea. Estos, al contrario de las funciones, no van a devolver ningún valor. También los lenguajes de programación van a contar con un amplio número de procedimientos previamente implementados.

Los procedimientos dentro de los algoritmos son llamados por su nombre como si fuera una instrucción.

A pesar de ser bloques de código independientes, ambos tipos de subprogramas van a mantener una comunicación con el algoritmo principal. Esta va a ser posible a través de los parámetros.

Los parámetros de los subprogramas van a permitir el flujo de información entre estos y el algoritmo principal, dicho flujo podrá ser en ambas direcciones. Por lo tanto en dependencia de la dirección en que viaje la información los parámetros se podrán clasificar en:

  • Parámetros de entrada: Estos van a ser los datos que le son suministrados por el algoritmo principal al subprograma, este último hará uso de ellos durante su ejecución.

  • Parámetros de salida: Estos van a ser suministrados por el subprograma al algoritmo principal. En el caso de una función estos parámetros se utilizan para aumentar el número de salidas, ya que esta normalmente sólo tiene una y es por su nombre.

  • Parámetros de entrada-salida: Este tipo de parámetro es un caso especial de los de salida. El algoritmo principal aprovecha un parámetro de salida para enviar datos al subprograma, este último lo procesa y altera su valor, produciéndose así el flujo en ambas direcciones.

El símbolo utilizado en los diagramas de bloques para la representación de los subprogramas dentro de un algoritmo es:

edu.red

Ejercicios propuestos.

Solución lineal

  • 1- Confeccione un algoritmo donde dada una cantidad X de naranjas imprima cuantas quedarían después de extraerle Y.

  • 2- Se asume que una caja tiene 5 caramelos. Elabore un algoritmo que calcule e imprima cuantos caramelos se vendieron en X cajas.

  • 3- Construya un algoritmo que pida al usuario que piense un número y que le realice una serie de operaciones matemáticas que se le irán indicando. Al final dicho algoritmo debe pedir el número resultante y ser capaz de mostrar el número pensado por el usuario.

  • 4- Elabore un algoritmo que pida un número e imprima su antecesor y su sucesor.

Solución condicionada

  • 1- Haga un algoritmo que entrado un número cualesquiera imprima si es positivo o negativo.

  • 2- Confeccione un algoritmo donde entrado un número cualquiera imprima si es par o impar.

  • 3- Dado tres números, elabore un algoritmo que imprima si pueden ser valores de los ángulos internos de un triángulo. (Tenga en cuenta que la suma de los ángulos interiores de todo triángulo es de 180o)

  • 4- Conocidas las longitudes de los tres lados de un triángulo, elabore un algoritmo que diga su clasificación teniendo en cuenta lo siguiente:

Escaleno: Todos sus lados son diferentes.

Isósceles: Dos lados iguales y uno diferente.

Equilátero: Los tres lados iguales.

  • 5- Conocidas las notas A y B de un estudiante, haga un algoritmo que:

  • a) Si A>B calcule y muestre el promedio.

  • b) Si A

  • c) Si A=B calcule y muestre su producto.

  • 6- Elabore un algoritmo que entrados dos números imprima el mayor.

  • 7- Confeccione un algoritmo que dados tres números cualesquiera:

  • a) Calcule e imprima la suma de ellos si son iguales.

  • b) Calcule e imprima el producto de ellos si solamente dos de ellos son iguales.

Partes: 1, 2, 3
 Página anterior Volver al principio del trabajoPágina siguiente