lunes, 6 de mayo de 2013

Página 8. Estructuras de Datos en C++



  • 1.7. PROGRAMACIÓN ESTRUCTURADA
La programación orientada a objetos se desarrolló para tratar de paliar diversas limitaciones que se encontraban en anteriores enfoques de programación. Para apreciar las ventajas de la POO, es preciso constatar las limitaciones citadas y cómo se producen con lo lenguajes de programación tradicionales.

C, Pascal y Fortran, y lenguajes similares, se conocen como lenguajes procedimentales (por procedimientos). Es decir, cada sentencia o instrucción señala al compilador para que realice alguna tarea: obtener una entrada, producir una salida, sumar tres números, dividir por cinco, etc. En resumen, un programa en un lenguaje procedimental es un conjunto de instrucciones o sentencias. En el caso de pequeños programas, estos principios de organización (denominados paradigma) se demuestran eficientes. El programador sólo tiene que crear esta lista de instrucciones en un lenguaje de programación, compilar en la computadora y está, a su vez, ejecuta estas instrucciones.

Cuando los programas se vuelven más grandes, cosa que lógicamente sucede cuando aumenta la complejidad del problema a resolver, las lista de instrucciones aumenta considerablemente, de modo tal que el programador tiene muchas dificultades para controlar ese gran número de instrucciones. Los programadores pueden controlar, de modo normal, unos centenares de líneas de instrucciones. Para resolver este problema los programas se descompusieron en  unidades más pequeñas que adoptaron el nombre de  funciones (procedimientos, subprogramas o subrutinas en otros lenguajes de programación). De este modo un programa orientado a procedimientos se divide en funciones, de modo que cada función tiene un propósito bien definido y resuelve una tarea concreta, y se diseña una interfaz claramente definida (el prototipo o cabecera de la función) para su comunicación con otras funciones.

Con e paso de los años, la idea de romper un programa en funciones fue evolucionando y se llegó al agrupamiento de las funciones en otras unidades más grandes llamadas módulos (normalmente, en el caso de C, denominadas archivos o ficheros); sin embargo, el principio seguía siendo el mismo: agrupar componentes que ejecutan listas de instrucciones (sentencias). Esta característica hace que a medida que los programas se hacen más grandes y complejos, el paradigma estructurado comienza a dar señales de debilidad y resulta muy difícil determinar los programas de un modo eficiente. Existen varias razones de la debilidad de los programas estructurados para resolver problemas complejos. Tal vez las dos razones más evidentes son éstas: primero, las funciones tienen acceso ilimitado a los datos globales; segundo, las funciones inconexas y datos, fundamentos del paradigma procedimental, proporcionan un modelo pobre del  mundo real.
  • 1.7.1. Datos locales y datos globales
En un programa procedimental, por ejemplo escrito en C, existen dos tipos de datos. Datos locales que son ocultos en el interior de la función y son utilizados, exclusivamente, por la función. Estos datos locales están estrechamente relacionados con sus funciones y están protegidos de modificaciones por otras funciones.

Otros tipos de datos son los datos globales a los cuales se puede acceder desde cualquier función del programa. Es decir dos o más funciones pueden acceder a los mismos datos siempre que éstos sean globales. En la Figura 1.3 se muestra la disposición de variables locales y globales en un programa procedimental.

Un programa grande (Figura 1.4) se compone de numerosas funciones y datos globales y conlleva una multitud de conexiones entre funciones y datos que dificulta su comprensión y lectura.

Todas estas conexiones múltiples originan diferentes problemas. En primer lugar, hacen difícil diseñar la estructura del programa. En segundo lugar, el programa es difícil de modificar ya qué cambios en datos globales pueden necesitar la reescritura de todas las funciones que acceden a los mismos. También puede suceder que estas modificaciones de los datos globales pueden no ser aceptadas por todas o algunas de las funciones.

Figura 1.3. Datos locales y globales

Figura 1.4. Un programa procedimental

  • 1.7.2. Modelado del mundo real
Otro problema importante de la programación estructurada reside en el hecho de que la disposición separada de datos y funciones no se corresponden con los modelos de las cosas del mundo real. En el mundo físico se trata con objetos físicos tales como personas, autos o aviones. Estos objetos no son como los datos ni como las funciones. Los objetos o no del mundo real tienen atributos y comportamiento.


Los atributos o características de los objetos son, por ejemplo; en las personas, su edad, su profesión, su domicilio, etc.; en un auto, la potencia, el número de la matrícula, el precio número de puertas, etc.; en una casa la superficie, el precio, el año de construcción, la dirección, etcétera. En realidad, los atributos del mundo real tienen su equivalente en los datos de un programa; toman un valor específico, tal como 200 metros cuadrados, 20.000 dólares, cinco puertas, etc.

El comportamiento son las acciones que ejecutan los objetos del mundo real como respuesta a un determinado estímulo. Si usted pisa los frenos de un auto, el coche(carro) se detiene; si acelera, el auto aumenta su velocidad, etc. El comportamiento, en esencia, es como una función: se llama a una función para hacer algo (visualizar la nómina de los empleados de una empresa).

Por esta razones, ni los datos ni las funciones, por sí mismas, modelan los objetos del mundo real de un modo eficiente.

La programación estructurada mejora la claridad,  fiabilidad y facilidad de mantenimiento de los programas; sin embargo, para programas grandes o a gran escala presentan retos de difícil solución.


 

martes, 30 de abril de 2013

Página 7. Estructura de Datos en C++



  • 1.6. TIPOS ABSTRACTOS DE DATOS.
Algunos lenguajes de programación tienen características que nos permiten ampliar el lenguaje añadiendo sus propios tipos de datos. Un tipo de dato definido por el programador se denomina tipo abstracto de datos (TAD) para diferenciarlo del tipo fundamental (predefinido) de datos. Por ejemplo, en C++ el tipo Punto, que representa las coordenadas x e y de un sistema de coordenadas rectangulares, no existe. Sin embargo, es posible implementar el tipo abstracto de datos, considerando los valores que se almacenan en las variables y qué operaciones están disponibles para manipular estas variables. En esencia un tipo abstracto de datos es un tipo que consta de datos (estructuras de datos propias) y operaciones que se pueden realizar sobre esos datos. Un TAD se compone de estructuras de datos y los procedimientos o funciones que manipulan esa estructuras de datos. 


Un tipo abstracto de datos puede definirse mediante la ecuación:
TAD = Representación (datos) + Operaciones (funciones y procedimientos)

La estructura de de tipo de dato abstracto, desde un punto de vista global, se compone de la interfaz y de la implementación (Figura 1.2).

Las estructuras de datos reales elegidas para almacenar la representación de un tipo abstracto de datos son invisibles a los usuarios o clientes. Los algoritmos utilizados para implementar cada una de las operaciones de los TAD, están encapsuladas dentro de los propios TAD.
La característica de ocultamiento de la información significa que los objetos tienen interfaces públicos. Sin embargo, las representaciones e implementaciones de esos interfaces son privados.



Método 1
Método 2
Método 3
Método 4

Interfaz pública


Representación
Estructura de datos
Variables (instancia)


Implementación de métodos:
   Código del método 1
   Código del método 2
   Código del método 3
   Código del método 4

Implementación privada

Figura 1.2. Estructura de un tipo abstracto de datos (TAD).
  • 1.6.1. Ventajas de los tipos abstractos de datos
Los tipos abstractos de datos proporcionan numerosos beneficios al programador, que se pueden resumir en los siguientes:
  1. Permite una mejor conceptualización y modelización (modelo) del mundo real. Mejora la representación y la  comprensibilidad. Clasifica los objetos basados en estructuras y comportamientos comunes.
  2. Mejora la robustez del sistema. Permiten la especificación del tipo de cada variable, de tal forma que se facilita la comprobación de tipos para evitar errores de tipo en tiempo de ejecución.
  3. Mejora el rendimiento (prestaciones). Para sistemas tipificados, el conocimiento de los objetos permite la optimización de tiempo de compilación.
  4. Separa la implementación de la especificación. Permite la modificación y la mejora de la implementación sin afectar a la interfaz público del tipo abstracto de dato.
  5. Permite la extensibilidad del sistema. Los componentes de software reutilizables son más fáciles de crear y mantener.
  6. Recoge mejor la semántica del tipo. Los tipos abstractos de datos agrupan o localizan las operaciones y la representación de atributos.
Un programa que maneja un TAD, lo hace teniendo en cuenta las operaciones o funcionalidad que tiene, sin interesarse por la representación física de los datos. Es decir, los usuarios de un TAD se comunican con éste a partir de la interfaz que ofrece el TAD mediante funciones de acceso. Podría cambiarse la implementación de tipo de datos sin afectar al programa que usa el TAD ya que para el programa esta oculta.

Las unidades de programación de lenguajes que pueden implementar un TAD reciben distintos nombres:

Modula -2           módulo
Ada                      paquete
C++                     clase
C#                       clase
Java                    clase

Estos lenguajes definen la especificación del TAD, que declara las operaciones y los datos, y la implementación, que muestra el código fuente de las operaciones y que permanece oculto al exterior del módulo.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 1.6. Clase hora que tiene datos separados de tipo int para horas, minutos y segundos. Un constructor inicializará este dato a 0, y otro lo inicializará a valores fijos. Una función miembro deberá visualizar la hora en formato 11:59:59. Otra función miembro sumará dos objetos de tipo hora pasados como argumentos. Una función principal main() creará dos objetos inicializados y otro que no está inicializado. Se suman los dos valores inicializados y se deja el resultado en el objeto no inicializado. Por último se muestra el valor resultante.

#include <cstdlib>
#include <iostream>

using namespace std;

class hora
{
private:
       int horas, minutos, segundos
public
       hora(){hora = 0; minutos = 0; segundos = 0;}
       hora(int h, int m, int s){horas = h; minutos = m; segundos = s;}
       void(visualizar);
       void sumar(hora h1, hora h2);
};

void hora::visualizar()
{
       cout << horas << ":" << minutos << ":" << segundos << endl;
}

void hora::sumar(hora h1, hora h2)
{
       segundos = h2.segundos + h1.segundos;
       minutos = h2.minutos + h1.minutos + segundos / 60;
       segundos = segundos % 60;
       horas = h2.horas + h1.horas + minutos / 60;
       minutos = minutos % 60;
}

int main(int argc, char *argv[])
{
       hora h1(10,40,50), h2(12,35,40),h;
       h1.visualizar();
       h2.visualizar();
       h.sumar(h1,h2);
       h.visualizar();
       return 0;
}


  • 1.6.2. Especificación de los TAD
El objetivo de la especificación es describir el comportamiento del TAD; consta de dos partes, la descripción matemática del conjunto de datos, y de las operaciones definidas en ciertos elementos de ese conjunto de datos.

La especificación del TAD puede tener un enfoque informal, éste describe los datos y las operaciones relacionadas en lenguaje natural. Otro enfoque más riguroso, especificación formal, supone suministrar un conjunto de axiomas que describen las operaciones en su aspecto sintáctico y semántico.

  • 1.6.2.1. Especificación informal de un TAD
Costa de dos partes:

  1. Detallar en los datos de tipo, los valores que se pueden tomar.
  2. Describir las operaciones, relacionándolas con los datos.
El formato que generalmente se emplea, primero se especifica el nombre  del TAD y los datos:

TAD nombre del tipo (valores y su descripción)

A continuación, de cada una de las operaciones con sus argumentos, y una descripción funcional en lenguaje natural, con este formato:

Operación (argumentos).

Descripción funcional

A continuación se especifica, siguiendo esos pasos, el tipo abstracto de datos Conjunto:

TAD Conjunto (colección de elementos sin duplicidades, pueden estar en cualquier orden, se usa para representar los conjuntos matemáticos con sus operaciones).

Las operaciones básicas sobre conjuntos son las siguientes:


Conjuntovacio
Crea un conjunto sin elementos
Añadir(Conjunto, elemento)
Comprueba si el elemento forma parte del conjunto, en caso negativo se añade. La operación modifica al conjunto.
Retirar(Conjunto, elemento)
Si el elemento pertenezca al conjunto se retira. La operación modifica al conjunto.
Pertenece(Conjunto, elemento)
Verifica si el elemento forma parte del conjunto, en cuyo caso devuelve cierto.
Esvacio(Conjunto)
Verifica si el conjunto no tiene elementos, en cuyo caso devuelve cierto.
Cardinal(Conjunto)
Devuelve el número de elementos del conjunto.
Union(Conjunto, Conjunto)
Realiza la operación matemática unión de dos conjuntos. La operación devuelve un conjunto con los elementos comunes de ambos.


Se pueden especificar más operaciones sobre conjuntos, todo dependerá de la aplicación que se quiera dar al TAD.

Norma
La especificación informal de un TAD tiene como objetivo describir los datos del tipo y las operaciones según la funcionalidad que tienen. No sigue normas rígidas,simplemente indica, de forma comprensible, la acción que realiza cada operación.


  • 1.6.2.2. Especificación formal de un TAD
La especificación formal proporciona un conjunto de axiomas que describen el comportamiento de las operaciones. La descripción ha de incluir una parte de sintaxis, en cuanto a los tipos de argumentos y el tipo de resultado, y una parte de semántica que detalla para unos valores particulares de los argumentos de la expresión del resultado que se obtiene. La especificación formal ha de ser lo bastante potente para que cumpla el objetivo de verificar la corrección de la implementación del TAD.

El esquema que se sigue consta de una cabecera con el nombre del TAD y los datos:

TAD nombre del tipo (valores que toma los datos del tipo)

A continuación, la sintaxis de las operaciones que lista las operaciones mostrando los tipos de los argumentos y el tipo de resultado:

Operación {Tipo de argumento, ...} -> Tipo resultado

Se continua con la semántica de las operaciones. Éstas se construye dando unos valores particulares a los argumentos, a partir de los cuales se obtiene una expresión resultado. Éste puede tener referencias a tipos ya definidos, valores del tipo lógico o referencias a otras operaciones del propio TAD.

Operaciones (valores particulares argumentos) -> expresión resultado.

Al realizar la especificación formal siempre hay operaciones definidas por sí mismas, se denominan constructores del TAD. Mediante los constructores se generan todos los posibles valores del TAD. Normalmente, se elije como el constructor la operación que inicializa  (por ejemplo, Conjuntovacio en el TAD Conjunto), y la operación que añade un dato o elemento (operación común a la mayoría de los tipos abstractos de datos). Se acostumbra a marcar con un asterisco a las operaciones que son constructores.

A continuación, se especifica formalmente el TAD conjunto. Para formar la expresión resultado se hace uno, si es necesario, de la sentencia alternativa si - entonces - sino.

TAD Conjunto (colección de elementos sin duplicidades, pueden estar en cualquier orden, se usa para representar los conjuntos matemáticos con sus operaciones).

Sintaxis

*Conjuntovacio*                      ->     Conjunto
*Añadir(Conjunto, Elemento)*         ->     Conjunto
Retirar(Conjunto, Elemento)          ->     Conjunto
Pertenece(Conjunto, Elemento)        ->     boolean
Esvacio(Conjunto)                    ->     boolean
Cardinal(Conjunto)                   ->     entero
Union(Conjunto, Conjunto)            ->     Conjunto

Semántica ∀ e1, e2 € Elemento y ∀ C, D € Conjunto

Añadir(Añadir(C, e1), e1)            ->     Añadir(C, e1)
Añadir(Añadir(C, e1), e2)            ->     Añadir(Añadir(C, e2), e1)
Retirar(Conjuntovacio, e1)           ->     Conjuntovacio
Retirar(Añadir(C, e1), e2)           ->     si e1 = e2 entonces
                                            C sino
                                            Añadir(Retirar(C, e2), e1)
Pertenece(Conjuntovacio, e1)         ->     falso
Pertenece(Añadir(C, e2), e1)         ->     si e1 = e2 entonces cierto sino
                                            Pertenece(C, e1)
Esvacio(Conjuntovacio)               ->     cierto
Esvacio(Añadir(C, e1))               ->     falso
Cardinal(Conjuntovacio)              ->     cero
Cardinal(Añadir(C, e1))              ->     si Pertenece(C, e1) entonces
                                            Cardinal(C) sino 1 + Cardinal(C)
Union(Conjuntovacio, Conjuntovacio)  ->     Conjuntovacio
Union(Conjuntovacio, Añadir(C, e1))  ->     Añadir(C, e1)
Union(Añadir(C, e1), D)              ->     Añadir(Union(C,D), e1)







viernes, 26 de abril de 2013

Página 6. Estructura de Datos en C++



  • 1.5. ABSTRACCIÓN EN LENGUAJES DE PROGRAMACIÓN
Los lenguajes de programación son las herramientas mediante las cuales los diseñadores de programas pueden implementar los modelos abstractos. La abstracción ofrecida por los lenguajes de programación se pueden dividir en dos categorías: abstracción de datos (perteneciente a los datos) y abstracción de control  (perteneciente a las estructuras de control).

Desde comienzo del decenio de los sesenta, en que se desarrollaron los primeros lenguajes de programación de alto nivel, ha sido posible utilizar las abstracciones más primitivas de ambas categorías (variables, tipos de datos, funciones, control de bucles (lazos), etc.).

  • 1.5. Abstracciones de control
Los microprocesadores ofrecen directamente sólo dos mecanismos para controlar el flujo y ejecución de las instrucciones: secuencia y  salto. Los primeros lenguajes de programación de alto nivel introdujeron las estructuras de control: sentencias de bifurcación (if) y bucles (for, while, do-loop, do-while, etc.).

Las estructuras de control describen el orden en que se ejecutan las sentencias o grupos de sentencia (unidades de programa). Las unidades de programa se utilizan como bloques básicos de la clásica descomposición "descendente". En todos los casos, los subprogramas sustituyen una herramienta potente de abstracción ya que durante su implementación, el programador describe en detalle cómo funcionan. Cuando el subprograma se llaman, basta con conocer lo que hace y no cómo lo hace. De este modo se convierten en cajas negras que amplían el lenguaje de programación a utilizar. En general, los subprogramas son los mecanismos más ampliamente utilizados para reutilizar código, a través de colecciones de subprogramas en bibliotecas.

Las abstracciones y estructuras de control se clasifican en estructuras de control a nivel de sentencia y a nivel de unidades. La abstracción de control a nivel de unidad se conoce como abstracción procedimental.

Abstracción procedimental (por procedimientos o funciones)

Es esencial para diseñar software modular y fiable. La abstracción procedimental se basa en la utilización de procedimientos o funciones sin preocuparse de cómo se implementan. Esto es posible sólo si conocemos qué hace el procedimiento; esto es, conocemos la sintaxis y semántica que utiliza el procedimiento o función. La abstracción aparece en los subprogramas debido a las siguientes causas:

  • Con el nombre de los subprogramas, un programador puede asignar una descripción abstracta que captura el significado global del subprograma. Utilizando el nombre en lugar de escribir el código permite al programador aplicar la acción en términos de su descripción de alto nivel en lugar de sus detalles de bajo nivel.
  • Los subprogramas proporcionan ocultación de la información. Las variables locales y cualquier otra definición local se encapsulan en el subprograma, ocultándolas realmente de forma que no se pueden utilizar fuera del subprograma. Por consiguiente el programador no tiene que preocuparse por las definiciones locales.
  • Los parámetros de los subprogramas, junto con la ocultación de la información anterior, permiten crear subprogramas que constituyen entidades de software propias. Los detalles locales de implementación pueden estar ocultos mientras que los parámetros se pueden utilizar para establecer la interfaz público.
En C++ la abstracción procedimental se establece  con los métodos o funciones miembro de clases.

Otros mecanismos de abstracción de control

La evolución de los lenguajes de programación ha permitido la aparición de otros mecanismos para la abstracción de control, tales como manejo de excepciones, corrutinas, unidades concurrentes o plantillas (templates). Estas construcciones las soportan los lenguajes de programación basados y orientados a objetos, tales como C++, C#, Java, Modula-2, Ada, Smalltalk o Eiffel.

  • 1.5.2. Abstracciones de datos
Los primeros pasos hacia la abstracción de datos se crearon con lenguajes tales como FORTRAN, COBOL y ALGOL 60, con la introducción de tipos de variables diferentes, que manipulan enteros, números reales, caracteres, valores lógicos, etc. Sin embargo estos tipos de datos no podrían ser modificados y no siempre se ajustan al tipo necesitado. Por ejemplo, el tratamiento de cadenas es una deficiencia en FORTRAN, mientras que la precisión y la fiabilidad para cálculos matemáticos es muy alta.

La siguiente generación de lenguajes, PASCAL, SIMULA-67 y ALGOL 68, ofreció una amplia selección de tipos de datos y permitió al programador modificar y ampliar los tipo de datos existentes mediante construcciones específicas (por ejemplo, arrays y registros). Además SIMULA-67 fue el primer lenguaje que mezcló datos y procedimientos mediante la construcción de clases, que eventualmente se convirtió en la base del desarrollo de programación orientada a objetos.

La abstracción de datos es la técnica de programación que permite inventar o definir nuevos tipo de datos (tipos de datos definidos por el usuario) adecuados a la aplicación que se desea realizar. La abstracción de datos es una técnica muy potente que permite diseñar programas más cortos, legibles y flexibles. La esencia de la abstracción es similar a la utilización de un tipo de dato, cuyo uso se realiza sin tener en cuenta cómo está representado o implementado. 

Los tipos de datos son abstracciones y el proceso de construir nuevos tipos se llaman abstracciones de datos. Los nuevos tipos de datos definidos por el usuario se llaman tipos abstractos de datos. (ADT Abstract Data Types).

El concepto de tipo, tal como se definió en PASCAL y ALGOL 68, ha construido un hito importante hacia la realización de un lenguaje capaz de soportar programación estructurada.
Sin embargo, estos lenguajes no soportaban totalmente una metodología orientada a objetos. La abstracción de datos útil para este propósito, no sólo clasifica objetos de acuerdo a su estructura de representación, sino que se clasifican de acuerdo al comportamiento esperado. Tal comportamiento es expresable en términos de operaciones que son significativas sobre esos datos, y las operaciones son el único medio para crear, modificar y acceder a los datos.

En términos más precisos, Ghezzi indica que un tipo de dato definido por el usuario se denomina tipo abstracto de dato (TAD) si:
  • Existe una construcción del lenguaje que le permite asociar la representación de los datos con las operaciones que lo manipulan;
  • La representación del nuevo tipo de dato está oculta de las unidades de programa que lo utilizan.
Las clases de C++, C# y Java cumplen las dos condiciones, agrupa los datos junto a las operaciones y su representación queda oculta de otras clases.

Los tipos abstractos de datos proporcionan un mecanismo adicional mediante el cual se realiza una separación clara entre la interfaz y la implementación del tipo de dato. La implementación de un tipo abstracto de dato consta de:

1.- Representación: elección de las estructuras de datos.
2.- Operaciones: elecciones de los algoritmos.




domingo, 21 de abril de 2013

Página 5. Estructura de datos en C++



  • 1.4. ALGORITMOS
El término resolución de un problema se refiere al proceso completo que abarca desde la descripción inicial del problema hasta el desarrollo de un programa de computadora que lo resuelva. La resolución de un problema exige el diseño de un algoritmo que resuelva el problema propuesto. Los pasos para la resolución de un problema son:

  1. Diseño del algoritmo que describe la secuencia ordenada de pasos --sin ambigüedades-- que conducen a la solución de un problema dado. (Análisis del problema y desarrollo del algoritmo).
  2. Expresar el algoritmo como un programa en un lenguaje de programación adecuado. (Fase de codificación).
  3. Ejecución y validación del programa por la computadora.

Para llegar a la realización de un programa es necesario el diseño previo de un algoritmo indicando cómo hace el algoritmo la tarea solicitada, y eso se traduce en la construcción de un algoritmo. El resultado final del diseño es una solución que debe ser fácil de traducir a estructura de datos y estructuras de control de un lenguaje de programación específico.

Las dos herramientas más comúnmente utilizadas para diseñar algoritmos son: diagramas de flujo y pseudocódigos.

Diagrama de flujo (flowchart)

Es una representación gráfica de un algoritmo.

EJEMPLO 1.1. Diagrama de flujo que representa un algoritmo que lea el radio de un círculo, calcule su perímetro y su área.

Se declaran las variables reales r, longitud, y area, así como la constante pi.

constantes pi = 3.14
variables real: r, longitud, area




Pseudocódigo

En esencia el pseudocódigo se puede definir como un lenguaje de especificación de algoritmos.

------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 1.2. Realizar un algoritmo que lea tres números; si el primero es positivo calcule el producto de los tres números, y en otro caso calcule la suma.

Se usan tres variables enteras Numero1, Numero2, Numero3, en las que se leen los datos, y otras dos variables Producto y Suma en la que se calcula, o bien el producto, o bien la suma. El algoritmo que resuelve el problema es el siguiente.

Entrada Numero1, Numero2 y Numero3
Salida Suma o el Producto

inicio
1 leer los tres números Numero1, Numero2, Numero3
2 si el Numero1 es positivo
     calcular el producto de los tres números
     escribir el producto
3 si el número no es positivo
     calcular la suma de los tres números
     escribir la suma
fin


El algoritmo en pseudocódigo es:

algoritmo Producto_suma
variables
     entero: Numero1, Numero2, Numero3, Producto, Suma
inicio
     Leer (Numero1, Numero2, Numero3)
     si (Numero1 > 0) entonces
          Producto ← Numero1 * Numweo2 * Numero3
          Escribe ('El producto de los números es: ', Producto)
     sino
          Suma ← Numero1 + Numero2 + Numero3
          Escribe ('La suma de los números es: ', Suma)
     fin si
fin


El algoritmo escrito en C++ es:

#include <cstdlib>
#include <iostream>
using namespacestd;

int main(int argc, char *argv[])
{
     int Numero1, Numero2, Numero3, Prodcuto, Suma;
     cin >> Numero1 >> Numero2 >> Numero3;
     if (Numero1 > 0)
     {
          Producto = Numero1 * Numero2 *Numero3;
          cout << " El producto de los numeros es: " << Producto;
     }
     else
     {
          Suma = Numero1 + Numero2 + Numero3;
          cout << " La sumas de los numeros es: " << Suma;
     }
     return 0;
}


El algoritmo es la especificación concisa del método para resolver un problema con indicación de las acciones a realizar. Un algoritmo es un conjunto finito de reglas que dan una secuencia de operaciones para resolver un determinado problema. Es, por consiguiente, un método para resolver un problema que tiene en general una entrada  y una salida. Las características fundamentales que debe cumplir todo algoritmo son:
  • Un algoritmo debe de ser preciso e indicar el orden de realización de cada paso.
  • Un algoritmo debe de estar bien definido. Si se sigue un algoritmo dos veces, se debe de obtener el mismo resultado cada vez.
  • Un algoritmo debe de ser finito. Si se sigue un algoritmo, se debe terminar en algún momento; es decir, debe de tener un número finito de pasos .
La definición de algoritmo debe de describir tres partes: Entrada, Proceso, Salida.

------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 1.3. Diseñar un algoritmo que permita saber si un número entero positivo es primo o no. Un número es primo si sólo puede dividirse por si mismo y por la unidad (es decir, no tiene mas divisores que él mismo y la unidad). Por ejemplo 9, 8, 6, 4, 12, 16, 20, etc., no son primos ya que son divisibles por números distintos a ellos mismos y a la unidad. Así 9 es divisible por 3, 8 lo es por 2, etc. El algoritmo de resolución del problema pasa por dividir sucesivamente el número por 2, 3, 4, etc.

Entrada: dato n entero positivo
Salida: es o no primo

proceso:

1. Inicio.
2. Hacer x igual a 2 (x = 2, x variable que representa a los divisores del número que se                              buscan).
3. Dividir n por x (n/x).
4. Si el resultado de n/x es entero, entonces n no es un número primo y bifurcar (partir) al punto 7; en caso contrario, continuar el proceso.
5. Suma 1 a x (x ← x + 1).
6. Si x es igual n, entonces n es un número primo; en caso contrario bifurcar al punto 3.
7. Fin.

El algoritmo anterior escrito en pseudocódigo y C++ es el siguiente:


Algoritmo en pseudocódigo
Algoritmo en C++
algoritmo primo
inicio
     variables
          entero: n, x;
          lógico: primo;
     leer(n);
     x ← 2;
     primo ← verdadero;
     mientras primo y (x < n) hacer
      si n mod x <> 0 entonces
       x ← x + 1;
      sino
       primo ← falso
      fin si
     fin mientras
     si (primo) entonces
      escribe (‘es primo’)
     sino
      escribe (‘no es primo’)
     fin si
fin
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
   int n, x;
   bool primo;
   cin >> n;
   x = 2;
   primo = true;
   while (primo && (x < n ))
      if (n % x != 0)
         x = x + 1;
      else
         primo = false;
   if (primo)
      cout << “es primo”;
   else
      cout << “no es primo”;
   return 0;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 1.4.  Calcular la media de una serie de números positivos, suponiendo que los datos se leen desde una terminal. Un valor de cero --como entrada-- indicará que se ha alcanzado el final de la serie de números positivos.

El primer paso a dar en el desarrollo del algoritmo es descomponer el problema en una serie de pasos secuenciales. Para calcular una media se necesita sumar y contar los valores. Por consiguiente, el algoritmo en forma descriptiva es:

inicio
     1. Inicializar contador de números C y variable Suma S a cero (S 0, C0).
     2. Leer un número en la variable N (leer (N)).
     3. Si el numero leído es cero: (si (N = 0) entonces).
          3.1 Si se ha leído algún número (Si C > 0).
               - Calcular la media; (media  S/C)
               - Imprimir la media; (Escribe (media)).
          3.2 Si no se ha leído ningún número (Si C = 0).
               - Escribir no hay datos.
          3.3 Fin del proceso.
     4. Si el número leído no es cero: (Si (N <> 0 )) entonces
          - Calcular la suma; (S  S + N).
          - Incrementar en uno el contador de números; (C  C + 1).
          - Ir al paso 2.
fin

El algoritmo anterior escrito en pseudocódigo y en C++ es el siguiente:

Algoritmo escrito en pseudocódigo
Algoritmo escrito en C++
algoritmo media
  inicio
    variables
      entero: N, C, S;
      real: media;
  C 0;
  S 0;
  repetir
    leer (N)
   si N <> 0 entonces
    S S + N;
    C C + 1;
   fin si
  hasta N = 0
  si C > 0 entonces
   media S / C
   escribe (media)
  sino
   escribe (“no datos”)
  fin si
fin
#include <cstdlib>
#include <iostream>
using namespace std;
int main (int argc, char *argv[])
{
  int N, C, S;
  float media;
  C = 0; S = 0;
  do
  {
    cin >> N;
    if (N =! 0)
    {
      S = S + N;
      C = C + 1;
    }
  }while (N != 0)
  if (C > 0)
    {
      media = S / C;
      cout << media;
    }
  else
      cout << “no datos”;
  return 0;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 1.5 Algoritmo que lee un número entero positivo n, y suma los n primero números naturales.

Inicialmente se asegura la lectura del número natural n positivo. Mediante un contador C se cuentan los números naturales que se suman, y en el acumulador Suma se van obteniendo las sumas parciales. Además del diagrama de flujo se realiza un seguimiento para el caso de la entrada n = 5.

Variables Entero: n, Suma, C



El algoritmo anterior escrito en pseudocódigo es y C++ es el siguiente:

Algoritmo escrito en pseudocódigo
Algoritmo escrito en C++
algoritmo suma_n_naturales
  inicio
    variables
      entero: Suma, C, n;
  repetir
    leer (n)
  hasta n > 0
  C 0;
  Suma 0;
   fin si
  mientras C < n hacer
   C C + 1;
   Suma Suma + C;
  fin mientras
  escribe (Suma)
fin
#include <cstdlib>
#include <iostream>
using namespace std;

int main (int argc, char *argv[])
{
  int Suma, C, n;
  do
    cin >> n;
  while (n <= 0);
  C = 0; Suma = 0;
  While (C < n)
  {
    C = C + 1;
    Suma = Suma + C;
  }
  cout << Suma;
  return 0;
}