1. Introducción

En este apunte se describe una introducción a la resolución de problemas destacando la importancia de abstracciones y modelos. El apunte tambien define el concepto de algoritmo detallando como los algoritmos nos ayudan en la resolución de problemas por computadora. Así mismo pone énfasis en la tarea de diseño de un algoritmo. Se presentan conceptos teóricos y ejemplos, y al finalizar el apunte se incluyen una serie de ejercicios.

2. Problemas

Definimos problema como una discrepancia entre un estado actual y un estado deseado. Resolver un problema consiste entonces en encontrar una secuencia de pasos que nos permitan pasar de un estado a otro. La resolución de un problema no afecta a un problema particular sino a una clase de problemas. Cuando hablamos de resolución de problemas estamos refiriéndonos en realidad a un conjunto de problemas.

Para hallar soluciones a problemas se requiere habilidad, conocimiento y experiencia.

Un problema puede ser simple o complejo según quien tenga que hallar su solución. Las personas tienen distintas capacidades para hallar soluciones para diferentes clases de problemas.

Aunque a veces es posible hallar la solución de un problema aparentemente difícil de una forma puramente intuitiva ya que podemos tener raptos de inspiración, en general resulta conveniente entrenarnos en el uso de algunas estrategias y t ácticas que nos orienten en la búsqueda de soluciones de problemas.

Estas tácticas y estrategias no necesariamente nos conducirán a la solución, pero es probable que nos ayuden en menor o mayor medida.

La búsqueda de la solución de un problema es una tarea difícil de sistematizar. Cada problema puede presentarse en forma aparentemente aislada y frecuentemente no sabemos cómo encararlo.

Sin embargo existen algunas pautas útiles que, de ser seguidas, pueden ayudarnos para enfrentar el problema: por ejemplo seguir los pasos de polya, utilizar una plantilla, es fundamental encontrar una buena representación para el problema, emplear estrategias. La representación constituirá un modelo del problema que deseamos resolver.

Hallar una solución para un problema específico puede brindarnos pautas para resolver la clase de problemas a la cual pertenece el problema particular.

Si podemos resolver una clase de problemas, estamos en condiciones de hallar una solución para una instancia específica, de acuerdo a esta resolución.

3. Abstracciones y Modelos

El corazón de la tarea de resolver problemas reside en ser capaces de manejar la complejidad inherente a ellos. La economía nacional, el movimiento de las moléculas en un objeto físico simple, son problemas muy complejos y por lo tanto difíciles de resolver. Para disminuir la complejidad de los problemas se utiliza una técnica muy poderosa: la abstracción.

El proceso de abstracción permite la construcción de modelos. Un modelo describe las propiedades fundamentales de un sistema, descartando los detalles irrelevantes.

Las leyes del movimiento de Newton son un modelo de la realidad física. Este modelo abstrae la enorme complejidad del movimiento de las partículas de los objetos y describe las propiedades globales del comportamiento integrado.

Para muchos propósitos los movimientos detallados de las moléculas en un objeto son irrelevantes, ya que consideramos los movimientos de las moléculas en su conjunto. Por ejemplo, nosotros hablamos de la velocidad de un objeto (que es la velocidad promedio de todas sus moléculas), de su temperatura (que es una medida de la energía cinética del movimiento molecular). En estos casos estamos abstrayendo de los detalles irrelevantes para el problema que tenemos entre manos.

Para algunos propósitos estas abstracciones pueden resultar inadecuadas. Cuando intentamos entender las reacciones químicas, por ejemplo, debemos considerar un modelo mas detallado que describa la estructura atómica de los materiales.

Cada modelo contiene el detalle necesario para explicar el fenómeno bajo estudio. Para analizar el movimiento o la temperatura de un objeto podemos ignorar completamente su estructura molecular.

Notemos que en cada caso hemos definido un modelo adecuado para representar el fenómeno a estudiar. Estos modelos son distintos aún cuando la realidad es una sola y brindan una visión destacada del aspecto que se desea resaltar.

Por ejemplo, consideremos la construcción de un edificio de departamentos. Existirán varios modelos de acuerdo a diferentes puntos de vista, aunque est á claro que el edificio es uno solo. Para el ingeniero civil, una carta de la estructura general ser á suficiente representación, mientras que el arquitecto requerirá una serie de planos de los diferentes tipos de planta. Por otro lado, para la inmobiliaria encargada de la venta, probablemente será necesaria una maqueta con las distintas plantas y algunos otros detalles que permitan al comprador tener una visión global y dinámica del conjunto edilicio.

En la ciencia, la complejidad de la realidad puede ser entendida comprendiendo una cantidad de modelos -abstracciones- que la describen desde distintos puntos de vista.

Es necesario no ahondar demasiado en el proceso de abstracción a riesgo de caer en un modelo mentiroso, es decir, en nuestro afán por comprender fácilmente la realidad, tratamos de simplificarla a tal extremo que desechamos propiedades fundamentales. Por ejemplo, cuando se estudia la estructura de un átomo, se cae siempre en un gráfico que sitúa al núcleo en el centro rodeado por electrones que giran en órbitas perfectas. Hoy sabemos que este modelo es completamente irreal, pues los electrones intercambian constantemente energía saltando de órbita en órbita.

Los modelos cambian para darnos mejores aproximaciones de cómo se comporta el mundo real. Nuestros modelos aproximan una realidad física pero los modelos en sí nunca son verdad, siempre están sujetos al cambio al incorporar nuevo conocimiento.

El nivel de detalle requerido en un modelo depende de lo que pensemos hacer con él. Un modelo adecuado para tener un primer contacto con una cierta área de conocimiento, puede ser totalmente inadecuado o insuficiente si vamos a profundizar el estudio de problemas dentro de esa área. Es más, cierto modelo de la realidad puede ser adecuado para resolver algunos problemas y no para otros.

Es importante preservar siempre la calidad del modelo, es decir, respetar la realidad que se trata de representar. Así podemos hacer una serie de suposiciones iniciales y limitar nuestro modelo. Luego se podrá trabajar sobre el modelo y ver si la realidad se adapta al comportamiento previsto. Si así fuera, hemos hallado un buen modelo que podrá servir de base para realizar estudios m ás profundos.

4. Algoritmos

Queremos encontrar métodos generales capaces de resolver una **clase de problemas por eso diseñaremos algoritmos.

Podés ver un video resúmen sobre algoritmos en el siguiente enlace:

📝 ALGORITMO: Un algoritmo es un modelo de la resolución de un problema, o mejor aún, de una clase de problemas. Un conjunto de directivas precisas que indicarán exactamente cómo se obtendrán los resultados deseados.

Informalmente un algoritmo es un método para resolver un problema, o un conjunto de directivas o una fórmula que indicará exactamente cómo se obtendrán los resultados deseados. De acuerdo a esta definición, todos trabajamos continuamente con algoritmos, sólo que no somos tan formales como para llamarlos así.

El conjunto de instrucciones dadas para armar un triciclo a partir de sus componentes, el procedimiento para registrarse en un colegio, una receta para preparar una torta, son ejemplos válidos de algoritmos. Describamos un algoritmo que llamaremos shampoo

    1. Mojar el cabello.
    1. Aplicar shampoo.
    1. Masajear.
    1. Enjuagar.
    1. Repetir.

Existen dos puntos de vista desde los cuales debe ser considerado un algoritmo:

  • Por un lado esta la especificación del algoritmo. La especificación comprende precisamente al conjunto de acciones que permiten resolver una clase de problemas. Desde este punto de vista un algoritmo se puede pensar como una entidad estática.

  • Por otro lado debemos considerar la ejecución del algoritmo. Cuando alguien lleva a cabo las directivas indicadas por el algoritmo, hablamos de ejecución. Durante la ejecución estamos hallando la solución de un problema específico o de una instancia de la clase de problemas. La ejecución es un proceso dinámico

En adelante, cuando hablemos de algoritmos, nos estaremos refiriendo a la especificación y cuando nos refiramos a la dinámica de un algoritmo, diremos ejecución de algoritmos.

Formalmente, definiremos entonces el concepto de algoritmo como:

📝 ALGORITMO: Un algoritmo es una secuencia finita de operaciones precisas (i.e. no ambigüas) para resolver un clase de problemas.

Por secuencia de operaciones, entendemos que para cada paso del algoritmo, el siguiente está definido sin ambigüedad. Esto es, luego de completar un paso debemos saber siempre cuál será ejecutado a continuación. El orden de ejecución puede indicarse en la especificación, numerando los pasos con enteros positivos. Alternativamente el orden puede asumirse por la posición de las operaciones. Sin embargo, en muchas ocasiones podemos desear interrumpir esta secuencia.

En nuestro ejemplo el paso 5 presenta ambigüedades, al no indicar qué pasos deben ser repetidos. Sin embargo, para cualquier persona es obvio que no debe volver a mojar el cabello y no tendría sentido enjuagar nuevamente si no colocamos shampoo otra vez.

Inherente al concepto de secuencia de operaciones, subyace la idea de principio y fin. Todo algoritmo debe tener un punto inicial y por lo menos uno final.

Las propiedades de un algoritmo incluyen:

  • El algoritmo recibe una Entrada y genera una Salida.
  • Precisión: Cada paso esta precisamente explicitado.
  • Determinismo: A igual entrada, igual salida. Esto es válido incluso para cada paso.
  • Correctitud: Un algoritmo es correcto, si cumple con los tres puntos siguientes:
    • El algoritmo resuelve el problema para el cual fue diseñado: eficaz. La Eficacia es la «capacidad de lograr el efecto que se desea o se espera». La eficacia significa conseguir las metas establecidas (qué se hace);
    • Para cada entrada, el algoritmo produce la salida deseada.
      • El algoritmo termina en un tiempo finito: finitud. En otras materias se los estudiará desde el punto de vista de su tiempo de ejecución, eficiencia y complejidad. La eficiencia es la capacidad de conseguir las metas de la mejor manera posible (cómo se hace); y la efectividad es la mezcla perfecta de eficacia y eficiencia (conseguir las metas con unos recursos mínimos).
  • Generalización: Un algoritmo es aplicable a un conjunto de entradas.

Las operaciones que componen un algoritmo son llamadas primitivas, y deben ser entendibles para la persona o máquina que las ejecutará. Por lo tanto el conjunto de primitivas posibles para escribir un algoritmo depende de quién o qué lo ejecutará.

Por ejemplo, la siguiente receta es un método posible para preparar una tarta de manzanas:
1. Prepare la masa.
2. Prepare el dulce de manzanas.
3. Rellene la masa con el dulce.
4. Coloque la tarta en horno moderado por 30 minutos.

Para una persona familiarizada con la cocina esta receta puede ser aceptable. Sin embargo los pasos 1 y 2 presentarán ambigüedades para alguien sin experiencia en la cocina y deberán ser desarrollados con más detalle.

Para que un algoritmo pueda ser interpretado y ejecutado debe estar expresado entonces en función de acciones comprensibles. La clave está pues en definir el conjunto de acciones en términos de las cuales va a poder ser expresado el algoritmo.

Uno de los puntos fundamentales para decidir si un algoritmo es válido, es pues el hecho de que las acciones que lo componen sean primitivas.

Ahora bien, ¿cuándo un algoritmo puede ser interpretado y ejecutado por una computadora?

5. Construcción de Programas

Hemos dicho que estamos en presencia de un problema cuando existe una discrepancia entre la situación actual y la situación deseada.

La resolución de un problema consiste en hallar un algoritmo o un método que nos permita pasar de la situación actual a la deseada. Este método permite en realidad resolver una clase de problemas, mas que un problema particular.

Existen clases de problemas, para los cuales es muy difícil hallar un método de resolución. Tomemos por ejemplo, un detective cuyo problema es hallar el asesino de un crimen. El detective por supuesto se basará en sus experiencias anteriores y seguramente considerará algunos casos similares ya resueltos. Es más, es posible que los primeros pasos sean siempre los mismos: averiguar cómo, cuándo y dónde se produjo el crimen, quiénes fueron los últimos que vieron a la víctima con vida… De acuerdo a las respuestas el caso tomará rumbos diferentes. Resulta muy difícil encontrar un algoritmo para resolver crímenes. En el mejor de los casos el detective podrá hallar la solución para cada caso particular.

Existen otras clases de problemas, sin embargo, para los que una vez hallada una solución para una instancia especifica, es fácil inferir un método de resolución para toda la clase de problemas.

Supongamos que nos enfrentamos al problema de buscar en una guía telefónica el número de teléfono de Pérez Juan. Para realizar esta tarea tenemos que partir de dos supuestos: sabemos leer y contamos con una guía.

A partir de estos dos hechos podemos hallar una solución al problema de una forma muy rudimentaria y tediosa:

Podemos comparar el primer nombre con Pérez Juan, si coinciden (saber leer implica poder decidir si dos nombres son iguales) hemos hallado la solución. Si no, consideramos el segundo nombre y así siguiendo hasta encontrar a Pérez Juan o hasta que se agotó la guía (la persona no figura).

Este método es muy **ineficiente porque partimos de premisas muy simples. Si disponemos de más información el problema se simplifica.

Si sabemos que la guía esta ordenada alfabéticamente y conocemos el alfabeto, nuestro algoritmo podría ser descartar por mitades:

Podemos abrir la guía por la mitad, aproximadamente, y comparar Pérez Juan con un nombre cualquiera. Pueden darse tres circunstancias:

El proceso se repite nuevamente hasta hallar a Pérez Juan o agotar la guía.

Si tenemos conocimiento de la distribución de los apellidos en la guía, podríamos descartar una parte de la misma de distinta manera:

Podemos abrir la guía considerando la posición de P en el alfabeto y luego buscar secuencialmente por hojas y luego, una vez quehemos localizado la página, avanzar por nombre.

En ningún idioma existe la misma cantidad de apellidos para cada letra. En castellano, si buscamos un apellido que comienza con P, descartaremos bastante más que las primeras 17/27 partes de la guía, aún cuando la posición de P en el alfabeto es 17, provisto que buena parte de los nombres comienzan con las primeras letras del alfabeto. Diferente situación se hubiera presentado en otro idioma.

Cada método ha sido una mejora respecto al anterior, pero requiere más Información, supone que sabemos hacer más cosas. En cada caso, además, hacemos uso de mayores conocimientos y de operaciones más complejas.

Cualquiera de los algoritmos sirve no sólo para intentar localizar a Pérez Juan en la guía, sino a cualquier persona. No sólo podemos hallar la solución de un problema, sino resolver toda una clase de problemas. Es más, el mismo método podría ser utilizado para hallar solución a problemas similares, como por ejemplo buscar una palabra en el diccionario.

De hecho, la confección de una guía presupone que la tarea de buscar un número telefónico se va a llevar a cabo frecuentemente. El esfuerzo empleado en encontrar un algoritmo se ve compensado por el hecho de que va a ser utilizado muchas veces.

Tomemos otro ejemplo. Cuando deseamos calcular el cociente q de un división entera, la forma más sencilla es hallarlo por restas sucesivas. El método es sencillo pero lento. Si esta tarea va a ser llevada a cabo muchas veces conviene encontrar un método más eficiente, aunque menos simple. En cualquier caso saber dividir, conocer el método, no implica entender el concepto.

Notemos que la acción de dividir, por definición, produce dos valores, el cociente q y el resto r. Aunque frecuentemente nos referimos indistintamente a hallar el cociente o a dividir, no son acciones equivalentes. La división entera de dos números a y b implica hallar otros dos números q y r tales que a = q*b + r.

Dos métodos para resolver un mismo problema normalmente difieren en el tipo de cosas que hay que saber hacer para llevarlos a cabo.

Para que una computadora pueda ser utilizada para hallar la solución de un problema, es necesario que sepa resolver el problema. Es decir, es necesario que se le brinde un método para resolver dicho problema. ¿Cómo explicitaremos dicho método?

La tarea de programar implica precisamente la obtención de un modelo computacional para la resolución del problema. Esto es, lograr un conjunto de instrucciones comprensibles para la computadora que permitan alcanzar la solución deseada para un conjunto de problemas.

Podemos pensar que:

  • No tiene sentido escribir programas puntuales que permitan hallar soluciones para problemas específicos como por ejemplo, buscar el número de teléfono de Pérez Juan o resolver la ecuación x\(^{2}\) + 3x - 1 = 0.
  • Estaríamos buscando un método para resolver una clase de problemas con una única instancia. Recordemos que, de acuerdo a lo dicho en la sección anterior, un algoritmo es un ente estático que describe una serie de pasos a realizar para resolver una clase de problemas. Cuando el algoritmo se ejecuta se resuelve una instancia particular de dicha clase.
  • Para que un algoritmo, destinado a resolver un problema, pueda ser transformado en un programa, es necesario que seamos capaces de definirlo en términos de operaciones que la computadora sepa hacer. Por ejemplo podemos encontrar un método para cambiar la cubierta de un auto, pero difícilmente podremos describir este método en términos de acciones que una computadora común pueda ejecutar.

Existe otra diferencia fundamental entre los problemas cotidianos planteados en la sección anterior y aquellos que pueden ser resueltos de manera tal de poder ser interpretados y ejecutados por una computadora.

Vimos que un algoritmo es una secuencia de pasos llevada a cabo sobre ciertos datos y que luego de un número finito de pasos produce un resultado. En los algoritmos vistos para lavar el cabello o preparar una tarta era un poco difícil distinguir datos y acciones.

En estos ejemplos no podemos hablar de datos, sino únicamente de estado inicial y estado final. Para el primer ejemplo el estado inicial era cabello sucio y el estado final cabello limpio. Sin embargo, esta especificación es ambigüa y por lo tanto la ejecución del algoritmo provocará variaciones en el estado final, dependientes del estado inicial y de la interpretación de las acciones.

Cuando pensamos en llevar un algoritmo a la computadora, estas ambigüedades no son admisibles. Los datos de entrada y de salida, así como las acciones, deben estar perfectamente definidos.

Los datos de entrada pueden considerarse como la descripción del estado inicial de un problema. Sin embargo en algunos problemas es difícil identificar el estado inicial con un conjunto de datos de entrada.

Los datos de salida pueden considerarse como la descripción del estado final de un problema. Sin embargo en algunos problemas es difícil identificar el estado final con un conjunto de datos de salida.

Un dato representa a un conjunto de valores. En la ejecución del algoritmo cada dato de entrada tomará un valor dentro del conjunto. Cada instancia diferirá de otra en función de los valores que tomen los datos. Los algoritmos que trataremos producirán los mismos resultados cuando se ejecuten para los mismos valores de los datos de entrada. La ejecución de una misma receta no siempre provocará como resultado la misma tarta, porque hay una serie de factores que determinan el estado final y son difíciles de especificar.

Aunque los puntos anteriores son muy importantes, la clave fundamental reside en ser capaces de hallar el método de resolución del problema.

Resolver un problema, o mejor dicho una clase de problemas, consiste precisamente en encontrar un método que permita hallar la solución de cualquier problema de la clase.

En ocasiones el mismo planteo del problema incluye la descripción del método para resolverlo. En otros casos, el enunciado no indica cómo resolver el problema, pero existen métodos para resolverlo. Sin embargo con frecuencia, el problema no especifica el método y el diseño del algoritmo es entonces nuestra responsabilidad.

Cuando el algoritmo puede ser transformado en una secuencia de instrucciones ejecutables por una computadora, se transforma en un programa.

Un programa estará escrito en un lenguaje de programación, es decir, en una notación formal y estricta, que podrá ser interpretada por la computadora.

Es posible establecer una analogía entre los pasos de Polya para resolver problemas y los pasos de la ingeniería de software:

Si intentamos obtener un algoritmo como una secuencia de acciones primitivas, y nos interesa transformar el algoritmo en un programa, el conjunto de acciones primitivas, para el programa queda definido en función del lenguaje de programación.

Un problema se puede resolver por métodos distintos, con distintos grados de eficiencia, como mostrábamos en el ejemplo de la búsqueda de un número en una guía telefónica.

Importante Debemos distinguir siempre la etapa de DISEÑO de la etapa de IMPLEMENTACIÓN. Si entendemos los conceptos de programación y como estos se emplean en la etapa de ANÁLISIS y DISEÑO, luego los podremos aplicar a cualquier lenguaje de programación (a cualquier IMPLEMENTACIÓN)

Cuando se usa una computadora es deseable minimizar el consumo de recursos. Nos referimos principalmente al costo en tiempo y espacio. Frecuentemente, en ciencias de la computación, la pregunta no es cómo resolver un problema sino cómo resolverlo de modo eficiente. Distintos algoritmos para resolver el mismo problema son evaluados en función de su eficiencia relativa y se busca el más rápido, el que consume menos espacio o soluciones de compromiso entre ambos costos.

En nuestra materia utilizaremos pseudocódigo en la etapa de diseño de un algoritmo, y una vez que el algoritmo est' e especficado en pseudocódigo, lo implementaremos utilizando un lenguaje de programación (en nuestro caso JAVA). Podemos utilizar cualquier lenguaje de programación a partir de un algoritmo escrito en pseudocódigo ya que la herramienta de diseño es independiente de cualquier lenguaje de programación. Para realizar la verificación del programa utilizaremos Trazas. En este apunte explicaremos como construir algoritmos con pseudocódigo, como realizar trazas, y veremos algunos conceptos asociados del lenguaje de programación que emplearemos.

6. Diseño de Algoritmos

Los componentes mas significativos de un algoritmo son:

📝 VARIABLE: Nombre o etiqueta usado para identificar una ubicación en donde puede almacenarse un dato.

📝 TIPO DE DATO: Determina los valores que puede tomar una variable y las operaciones que pueden realizarse con ella. Ejemplos de tipo de datos son: entero, real, lógico, carácter

Se agrega una definici' on adicional: Un Dato es un objeto sobre el cual opera un algoritmo. Para resolver un problema mediante computadora, es necesario identificar datos e incógnitas para luego dise ñar el algoritmo, que implementado en un lenguaje de programación resolverá el problema.

Aca tenemos otra definición de variable: Una variable es un nombre que representa un contenedor de datos cuyo valor puede variar durante la ejecución del programa. En cambio una constante es un valor que se asigna una vez y se mantiene fijo para todo el algoritmo (o en el programa). Ambos son un espacio reservado en memoria para almacenar un valor. Ambos tienen un tipo de dato y un identificador.

📝 CONSTANTE: Representa un valor que es asignado una vez, y se mantiene fijo para todo el algoritmo o programa.

En un algoritmo, los datos de entrada son los que se van a procesar. Los datos de salida son datos derivados, es decir, obtenidos a partir de los datos de entrada.

A partir del enunciado de un problema identificamos los datos de entrada y las incógnitas.

El perímetro de un jardín rectangular es de 58 m. Si el lado mayor mide 11 m más que el lado menor. ¿Cuánto miden los lados del jardín? 
Plantilla Desarrollo
Identificador Obtener los valores de los ladoMenor y ladoMayor
Objetivo perimetro = perímetro del jardín = 58
ladoMayor = ladoMenor + 11
Representación perimetro = 2 ladoMayor + 2 ladoMenor
ladoMayor = ladoMenor + 11
Desarrollo
de Pasos

2 ladoMayor + 2 ladoMenor= 58
2 (ladoMenor + 11) + 2 ladoMenor = 58
2 ladoMenor +22 +2 ladoMenor = 58

ladoMenor = \(\frac{58-22}{4}\)
ladoMenor = 9
ladoMayor = 9 +11 = 20

Resultado \(ladoMayor\) =20
\(ladoMenor\) =9
Verificacion perimetro = 2 .ladoMayor + 2 .ladoMenor
58 = 2 . 20 + 2 . 9

De acuerdo a la plantilla que utilizamos ¿donde detectamos los datos de entrada del algoritmo y los datos de salida? En la plantilla los datos de entrada estaban presentes en Datos Relevantes, y los datos de salida estan presentes en el objetivo y el resultado del problema.

Para poder resolver el problema mediante un algoritmo necesitaremos recurrir a instrucciones que luego serán implementadas en el lenguaje de programación. Una instrucción es una orden o acción clara y precisa.

6.1 Asignación

La asignación es la instrucción que nos permite asociar un valor a una variable, es decir, nos permite almacenar un dato en una variable. Es la instrucción fundamental, dado que todo algoritmo puede verse como una composición de asignaciones. Su sintaxis es:

X \(\leftarrow\) E donde X es una variable y E puede ser:.

  • Un valor;
  • Una expresión que luego de ser evaluada arroja un resultado;
  • Otra variable a la cual le fue asignado un valor con anterioridad.

📝 ASIGNACIÓN: En la evaluación de una asignación primero se evalúa la expresión que esta en el lado derecho de la asignación, el valor obtenido se asigna a la variable que está en el lado izquierdo.

Debe quedar claro que la asignación no es una igualdad. El planteo de una igualdad, por ejemplo letra = ‘A’ (donde letra es una variable que asumira un valor de tipo carácter) representa una expresión lógica, que a la hora de ser evaluada, resultará verdadera, si el valor almacenado en la variable letra es el carácter ‘A’; de lo contrario resultará falsa.

En cambio una asignación implica almacenar información en una variable, p. ej., si entero es una variable entera en la cual deseamos almacenar el valor 8, podremos hacerlo con la instrucción:

entero \(\leftarrow\) 8

Y si posteriormente necesitamos obtener el sucesor del valor que actualmente contiene la variable entero, deberemos plantear la asignación:

entero \(\leftarrow\) entero + 1

En primer lugar se evalúa la expresión entero + 1 (con el valor actual de la variable entero, que es 8), y luego se produce la asignación. Esta instrucción logrará que el valor de entero luego de su ejecución sea 9.

Observar que entero = entero + 1 es una expresión lógica cuyo valor de verdad es falso, cualquiera sea el contenido de la variable entero, razón por la cual no tiene sentido utilizarla. En cambio, entero \(\leftarrow\) entero + 1 no sólo tiene sentido, sino que es imprescindible en algunas situaciones.

6.2. Instrucciones de Entrada y Salida

Estas instrucciones permiten la comunicación con el mundo exterior; permiten el ingreso de datos a un programa y la visualización de resultados del mismo.

Las instrucciones de entrada, al igual que la asignación, permiten asociar un valor a una variable, pero en el caso de la entrada, este valor es tomado desde el exterior. La sintaxis correspondiente es:

LEER(edad)

donde:edad es el nombre de una variable a la que deseamos asignarle un valor.

Por ejemplo, si \(num\) es una variable entera, podemos asignarle un valor de la siguiente forma: LEER(num)

De esta forma, el valor que será asociado a la variable \(num\) podrá ser distinto cada vez que se ejecute esta instrucción, mientras que si usamos una asignación nuestro programa obliga a la variable \(num\) a tener el mismo valor (el que aparece en dicha asignación) cada vez que se ejecute la instrucción.

Las instrucciones de salida permiten mostrar los resultados obtenidos por nuestro programa. De no existir este tipo de instrucciones, la solución del problema quedaría almacenada en alguna variable y no sería posible conocer su valor.

La sintaxis es: ESCRIBIR(edad)

donde: edad es el nombre de la variable que almacena la información que deseamos conocer.

📝 INSTRUCCIONES DE ENTRADA Y SALIDA: La instrucción de entrada es LEER(variable) y permite asociar un valor a una variable. La instrucción de salida es ESCRIBIR(variable) y permite mostrar el valor que la variable tiene en un momento determinado. Siempre debemos escribir las instrucciones LEER y ESCRIBIR, con mayúscula.


Diseñar un algoritmo para calcular el perímetro de un rectángulo dados los lados.  
Para el algoritmo cálculoPerímetro es importante primer pensar cuales son los datos de Entrada, Salida y Relaciones entre Datos. Si bien estos pasos no se requeriran en la solucion que Ud. brinde habitualmente es importante tener claros estos componentes de la soluci\' on:

El ALGORITMO sería:

ALGORITMO calcularPerimetro() RETORNA ∅
  (*alg. que lee datos de un rectangulo, 
  valores enteros mayores a 0, calc. perimetro *)
  REAL ladoMenor, ladoMayor, perimetro
  ESCRIBIR(”Ingrese el valor del lado menor”)
  LEER(ladoMenor)
  ESCRIBIR(”Ingrese el valor del lado mayor”)
  LEER(ladoMayor)
  perimetro ← 2*ladoMenor + 2* ladoMayor
  ESCRIBIR(”El perímetro del rectángulo de lados”+
         ladoMenor+” y ”+ladoMayor+” es”+perimetro)
FIN ALGORITMO calcularPerimetro 

6.3. Traza

La traza de un algoritmo se puede definir como la ejecución manual de forma secuencial de los pasos que lo componen. Así, la traza del algoritmo es el valor que van adoptando las ** variables** a medida que se va ejecutando. En una tabla representamos los sucesivos estados de todas las variables. Incluiremos en la última columna todo aquellos que se muestre por pantalla.

En nuestro ejemplo, si el usuario ingresa los datos 2 y 3 para ladoMenor y ladoMayor respectivamente, tendremos:

ladoMenor ladoMayor perimetro salida por pantalla
2 3 Ingrese el valor del lado menor
Ingrese el valor del lado mayor

Luego de la asignación perimetro \(\leftarrow\) 2 * ladoMenor+ 2 * ladoMayor el valor de perimetro se habrá modificado:

ladoMenor ladoMayor perimetro salida por pantalla
2 3 10 Ingrese el valor del lado menor
Ingrese el valor del lado mayor
El perimetro del rectangulo de lados 2 y 3 es 10

7. Implementación Java

En RPA utilizaremos el lenguaje de programación JAVA para realizar las implementaciones de nuestros algoritmos. Utilizaremos Netbeans, un entorno de desarrollo para JAVA, para programar. Es importante que descargues Netbeans en tu computadora, y lo instales. Podés descargarlo desde: [https://www.oracle.com/technetwork/java/javase/downloads/jdk-netbeans-jsp-3413139-esa.html] Deberan descargar aquella opción que corresponda con las caracteristicas de su equipo.

Podrás crear una aplicacion JAVA inicial siguiendo los pasos de este video:

En el video se utiliza la librería TecladoIN [https://opendata.fi.uncoma.edu.ar/rpa/TecladoIn.java], que es necesario incorporar al proyecto JAVA para realizar la entrada y salida de datos. Podés utilizar TecladoIn o podrás utilizar la clase Scanner en su reemplazo.

7.1. Introducción

Esta sección se incluye un resumen sobre algunos conceptos de Java que son elementales para comenzar a programar con el lenguaje. Se describe como especificar variables, constantes, tipos de dato, operadores y operandos, expresiones, el concepto de asignación en JAVA. Es una sección que muestra como los conceptos que fueron desarrollados en la sección anterior se aplican a un lenguaje de programación (en este caso JAVA).

Adicionalmente se resume en el apunte la sintáxis asociada a estos conceptos, características de entrada-salida, y la estructura básica de un programa JAVA.

7.2. Variable y Constantes

En JAVA las variables se definen especificando su tipo. Las variables pueden almacenar datos de un tipo primitivo (o un tipo clase ), y su valor puede cambiar durante la ejecución del programa tantas veces como sea necesario, además pueden aplicarse ciertos operaciones dependiendo del tipo de las variables.

Convención: las variables comienzan con minúsculas y las constantes van todas en mayúsculas.

Ejemplos de identificadores de variables: miNombre, jugadoresDeFutball, juan, notaFinal, promedio.

Ejemplos de identificadores de constantes: VALOR_PI, TASA_ANUAL, INCREMENTO

Convención: Para definir variables cuyos nombres son compuestos utilizaremos la notación infixCaps, la cual permite yuxtaponer dos palabras, comenzando la segunda palabra con su inicial en mayúscula.

Java es un lenguaje Case Sensitive, es decir que es sensible a las minúsculas y mayúsculas. En pocas palabras, para Java la variable contador es distinta a la variable Contador.

En Java las constantes son variables que a las que se les aplica el modificador final. El valor de una variable declarada como final no puede cambiar durante la ejecución de un programa. La diferencia que tiene Java con otros lenguajes es que si no inicializamos la constante en la definición, se podrá hacer más adelante, pero una vez establecido un valor éste no podrá ser cambiado. Así una variable que declaramos como final, puede tener valores distintos en dos ejecuciones de un mismo programa, pero solo un valor en cada ejecución.

Ejemplo:

final float PI = 3.14159;

7.3. Tipos de Dato

Un tipo de datos define el conjunto de valores que puede tomar una variable. El tipo de dato de una variable define cuáles son los valores que se pueden almacenar en la variable, por ejemplo en el caso de los números: enteros, reales, etc. Si una variable es de tipo entero, sólo podrá almacenar números enteros y no así reales, y tendrá definidas ciertas operaciones para manipular dichos valores. En cambio una variable de tipo real podrá almacenar un número entero, pues los enteros también son reales, y tendrá definido un conjunto de operaciones que podrán aplicarse.

Los tipos de datos primitivos definidos por el lenguaje de programación son atómicos, es decir que permiten almacenar un solo valor en cada variable.

En JAVA hay 8 tipos de datos primitivos:

Grupo Tipo de Datos Descripción
Reales
Enteros
Lógicos
Caracteres
Texto
double, float
byte, long, int, short
boolean
char
String
Son números que tienen punto flotante, es decir tienen decimales.
Son números que no tienen parte decimal.
Solo pueden tomar dos valores true o false.
Son letras, números, símbolos especiales que van entre comillas simples. | Son cadenas de caracteres.

7.4. Operadores y Operandos

Los operadores son símbolos que representan algún tipo de operación sobre dos operandos (salvo los operadores unarios que actúan sobre un solo operando) y arrojan un resultado. Se deben considerar los tipos de los operandos para determinar cuales operadores son aplicables. Por ejemplo que si los operandos son números el operador deberá ser un operador aritmético.

En Java existen varios tipos de operadores, los cuales representan operaciones que se realizan sobre operandos, y son listados en la siguiente Tabla:

Tipo de Operadores Aritméticos Relacionales Lógicos
Operadores * Multiplicación
\(\backslash\) División
% Módulo-Residuo
+ Adición
- Diferencia
\(>\) mayor que
\(<\) menor que
\(>=\) mayor igual que
\(<=\) menor igual que
\(==\) igual a (comparación)
\(!=\) distinto a
&& and
\(\|\) or
\(\wedge\) xor
! not

7.5. Expresiones

Una expresión es todo aquello que se puede evaluar, es decir que lanza un resultado. Las siguientes son expresiones:

  1. Variables
  2. Constantes
  3. Operando Operador Operando
  4. Expresión Operador Expresión

Considerando los tipos de operadores, podemos decir que existen expresiones de tipo aritméticas, relacionales y lógicas, es decir expresiones que al ser evaluadas retornan un resultado de un cierto tipo. En cada caso tanto los operadores como los operandos deben ser compatibles, de lo contrario no se podría evaluar. Por ejemplo si intentamos sumar dos valores que no son numéricos, esta sería una expresión incorrecta que no se podría evaluar.

7.6. Asignación

El operador de asignación es aquel que permite que la evaluación de una expresión se almacene en una variable. El tipo de la expresión debe ser de tipo compatible con el tipo de la variable. Se representa con el símbolo de igualdad ‘=’, pero no se debe confundir con el operador de comparación (en Java ‘==’).

7.7. Sintaxis

Declaración de variables

El formato es: tipoDeDato identificadorVariable;

Ejemplos:

int edad;
double sueldo;
boolean sexo;
char primeraVocal;

Asignación de variables

identificadorVariable = EXPRESION; Ejemplos:

edad = 23;
sueldo = 1500.50;
sexo = true;
primeraVocal = 'A';

Declaración y asignación de variables

tipoDeDato identificadorVariable = EXPRESION;

Ejemplos:

int anioNacimiento = 1982;
int anioActual = 2007;
int edad = anioActual - anioNacimiento;

7.8. Entrada Salida

Al hablar de entrada y salida nos referimos al ingreso de datos por parte del usuario y salida de la computadora por ejemplo en la pantalla al usuario.

Utilizaremos la clase Scanner para hacer el ingreso de datos y la salida de los mismos en pantalla. Para ello, todos nuestros algoritmos declararán al comienzo del programa, un objeto sc:

Scanner sc = new Scanner(System.in);

La siguiente tabla describe ejemplos de entrada y salida de variables de distinto tipo:

Primitiva Pseudocódigo Java
Entrada: Mostrar mensajes por pantalla
ESCRIBIR()
System.Out.print()
System.Out.println()

Salida: Leer datos del usuario
LEER()

Scanner sc = new Scanner(System.in);
Dependiendo del caso:

En general para leer datos desde teclado con Scanner podemos usar los métodos nextXxx() donde Xxx indica el tipo de dato a leer:

  • sc.nextByte() para leer un dato de tipo byte.
  • sc.nextShort() para leer un dato de tipo short.
  • sc.nextInt() para leer un dato de tipo int.
  • sc.nextLong() para leer un dato de tipo long.
  • sc.nextFloat() para leer un dato de tipo float.
  • sc.nextDouble() para leer un dato de tipo double.
  • sc.nextBoolean() para leer un dato de tipo boolean.
  • sc.next().charAt(0) para leer un character.
  • sc.nextLine() para leer un String hasta encontrar un salto de línea.
  • sc.next() para leer un String hasta el primer delimitador, generalmente hasta un espacio en blanco o hasta un salto de línea.

7.9. Estructura Básica de un Programa

Todo programa tiene una parte principal que dirige el funcionamiento del mismo. Utilizaremos el siguiente esquema general para definir todos nuestros programas.

ALGORITMO calcularPerimetro() RETORNA ∅
  (*alg. que lee datos de un rectangulo, 
  valores enteros mayores a 0, calc. perimetro *)
  REAL ladoMenor, ladoMayor, perimetro
  ESCRIBIR(”Ingrese el valor del lado menor”)
  LEER(ladoMenor)
  ESCRIBIR(”Ingrese el valor del lado mayor”)
  LEER(ladoMayor)
  perimetro ← 2*ladoMenor + 2* ladoMayor
  ESCRIBIR(”El perímetro del rectángulo de lados”+
         ladoMenor+” y ”+ladoMayor+” es”+perimetro)
FIN ALGORITMO calcularPerimetro 

import java.util.Scanner;
public class PrimerPrograma
{
// Parte principal de programa
public static void main( String[] args )
{
  // Declaración de variables
  double ladoMayor, ladoMenor, perimetro;
  Scanner sc = new Scanner(System.in);
  
  System.out.println("Calcula el perimetro de un rectángulo");
        
  // Pedimos un dato al usuario
  System.out.print("ingrese el lado mayor");
  // Leemos el dato del usuario
  ladoMayor = sc.nextDouble();
        
  System.out.print("ingrese el lado menor");
  ladoMenor = sc.nextDouble();
        
  // Calculamos el perimetro
  perimetro = ladoMayor *2 + ladoMenor * 2;
        
  // Mostramos por pantalla la edad del usuario
  System.out.println("El perimetro es "+perimetro);
}
}

Podes ver el siguiente video que explica como implementar el algoritmo de calcular perímetro en Netbeans:

Cada programa que realicemos debe estar guardado en un archivo con el nombre que le dimos en la línea de definición del mismo. En este caso el archivo donde guardaremos nuestro programa se llamará PrimerPrograma.java y al ser compilado se generará PrimerPrograma.class.

Debe recordar que Java es sensible a mayúsculas y minúsculas, por lo que el nombre del archivo debe coincidir incluso en las mayúsculas y minúsculas usadas.

Otro ejemplo:

ALGORITMO calcularEdad() RETORNA ∅
  (* Calculo impreciso de la edad *)
  ENTERO anioNacimiento, anioActual, edad
  anioActual <- 2021
  ESCRIBIR(”Ingrese el año de su nacimiento”)
  LEER(anioNacimiento)
  (* Calculamos la edad del usuario *)
   edad <- anioActual - anioNacimiento
  (* Mostramos por pantalla la edad del usuario *)
  ESCRIBIR("Su edad es: "+ edad)
FIN ALGORITMO calcularEdad

import java.util.Scanner;
public class PrimerPrograma
{
// Parte principal de programa
public static void main( String[] args )
{
// Declaración de variables
int anioNacimiento;
int anioActual = 2007;
int edad;
// Pedimos un dato al usuario
System.out.print("Ingrese el año de su nacimiento");
// Leemos el dato del usuario
anioNacimiento = sc.sc.nextInt();
// Calculamos la edad del usuario
 edad = anioActual - anioNacimiento;
// Mostramos por pantalla la edad del usuario
System.out.println("Su edad es: "+ edad);
}
}

8. Videos de clases