.. | ||
README.md |
PR 4
Volver a la página principal de "Fundamentos de la Programación"
Índice
17. Esquemas de recorrido y búsqueda
Ya hemos visto las estructuras secuencial, alternatica e iterativa, con esto, ya podemos resolver cualquier problema. Ahora vamos a ver esquemas de recorrido y búsqueda, que son una especie de patrones o estructuras predeterminadas para diseñar algoritmos de manera más sistemática y ganar en eficiencia y fiabilidad.
Un esquema es una especie de plantilla que nos permite solucionar un tipo de problema específico con adaptaciones a cada caso concreto. Un ejemplo de aplicación de esquemas de recorrido y búsqueda es para resolver problemas de tratamiento de secuencias de manera más eficiente y sistemática.
17.1. Aprocimación intuitiva
La idea es que, en lugar de ir a ciegas, sigamos un esquema de recorrido o búsqueda que nos permita ir avanzando de manera sistemática y eficiente. Por ejemplo, si tenemos que recorrer una matriz, podemos hacerlo por filas o por columnas, o si tenemos que buscar un elemento en una lista, podemos hacerlo de manera secuencial o binaria.
17.1.1. Ejemplo 1 - Temperatura media
Vamos a calcular la temperatura media de todo el año suponiendo que vamos leyendo por el canal de entrada las temperaturas medias de cada día:
algorithm averageTemp
var
temperature: real;
average: real;
i: integer;
end var
i:= 0;
average:= 0;
while i < 365 do
temperature:= readReal();
average:= average + temperature;
i:= i + 1;
end while
write(average / 365.0);
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
float temperature;
float average;
int i;
i = 0;
average = 0;
while (i < 365) {
scanf("%f", &temperature);
average = average + temperature;
i = i + 1;
}
printf("%f\n", average / 365.0);
return 0;
}
17.1.2. Ejemplo 2 - Recaudaciones de una sala de cine
Cargamos los datos de una tabla de recaudaciones de una sala de cine a partir de lo leído por el canal de entrada estándar.
const
MAX_THEATERS: integer = 20;
END_SEQ: real = -1.0;
end const
type
tTheater = record
collect: vector[MAX_THEATERS] real;
numTheaters: integer;
end record
end type
action fillTable(inout movieTheater:tTheater);
var
i: integer;
temp: real;
end var
temp:= readReal(); {We read the first value}
movieTheater.numTheaters:= 0;
while temp ≠ END_SEQ do
movieTheater.numTheaters:= movieTheater.numTheaters + 1;
movieTheater.collect[movieTheater.numTheaters]:= temp;
temp:= readReal();
end while
end action
#include <stdio.h>
#define END_SEQ -1
#define MAX_THEATERS 20
typedef struct {
float collect[MAX_THEATERS];
int numTheaters;
} tTheater;
void fillTable(tTheater *movieTheater) {
/* var definition */
int i;
float temp;
/* table initialization */
temp = 0;
movieTheater->numTheaters = 0;
scanf("%f", &temp); /* read the first value */
/* iteration while the read number is not -1 */
while (temp != END_SEQ) {
/* Save the read number in the table */
movieTheater->collect[movieTheater->numTheaters] = temp;
movieTheater->numTheaters = movieTheater->numTheaters + 1;
scanf("%f", &temp);
}
}
17.2. Esquema de recorrido
En los dos ejemplos anteriores estamos haciendo un recorrido a través de una serie de datos repitiendo el mismo esquema:
- Acceso al primer elemento. En el primer ejemplo, leemos el primer valor de la temperatura y en el segundo, leemos el primer valor de la tabla.
- Tratamiento inicial. Inicializamos las variables que utilizaremos para hacer el tratamiento posterior.
- Último elemento. Comprobamos si hemos llegado al último elemento.
- Trater elemento. Aplicamos las acciones necesarias para resolver cada uno de los datos a tratar.
- Acceso al siguiente elemento.
- Tratamiento final. Terminar las acciones que queden por resolver el problema.
Ejemplo 1
Partes | Código |
---|---|
Acceso al primer elemento | i := 1; |
Tratamiento inicial | media := 0; |
Comprobar si ya hemos llegado al último elemento | while i <= 365 do |
Tratar el elemento | temperatura := readReal(); media := media + temperatura; end while |
Acceso al siguiente elemento | i := i + 1 |
Tratamiento final | writeReal(media/365.0) |
Ejemplo 2
Partes | Código |
---|---|
Acceso al primer elemento | temp := readReal(); |
Tratamiento inicial | movieTheater.numTheaters := 0; |
Comprobar si ya hemos llegado al último elemento | while temp ≠ END_SEQ do |
Tratar el elemento | movieTheater.numTheaters := movieTheater.numTheaters + 1; movieTheater.collect[movieTheater.numTheaters] := temp; end while |
Acceso al siguiente elemento | temp := readReal(); |
Tratamiento final | - |
Con lo cual, en estos dos casos se comparte el esquema de recorrido, ya sea para recorrer datos que leemos del canal estándar o datos de una tabla. Un patrón habitual es el siguiente:
algorithm esquemaRecorrido
{acceder al primer elemento}
{tratamiento inicial}
while not {último elemento} do
{tratar elemento}
{acceder al siguiente elemento}
end while
{tratamiento final}
end algorithm
Tener en cuenta que:
- Todos los elementos de la secuencia son del mismo tipo.
- Procesamos todos los elementos de la misma manera.
- Es necesario saber cómo se acaba la secuencia.
17.3. Esquema de búsqueda
Vamos a ver un ejemplo que es lo más esclarecedor.
17.3.1. Ejemplo
A partir de las temperaturas medias de un año que leemos por el canal estándar, averiguar el primer día que ha helado. Debemos comprobar que la temperatura es negativa.
algorithm hasFrozen
var
t: vector[365] real;
frost: boolean;
i: integer;
end var
i:= 1;
frost:= false;
while i ≤ 365 and frost == false do
t[i]:=readReal();
if t[i] ≤ 0 then
frost:= true;
end if
i:=i+1:
end while
writeBoolean(frost);
end algorithm
#include <stdio.h>
#include <stdbool.h>
int main(int argc, char** argv) {
float t[365];
bool frost;
int i;
i = 0;
frost = false;
while (i < 365 && !frost) {
scanf("%f", &t[i]);
if (t[i] <= 0) {
frost = true;
}
i = i + 1;
}
printf("%d\n", frost); /* Equivalence in C language: 0 == false, 1 == true */
return 0;
}
Cuando encontramos el primer caso de temperatura negativa ya podemos dejar de buscar, hemos realizado una búsqueda. Otro caso sería si quisieramos saber cuántos días ha helado, que tendríamos que hacer un recorrido por todas las temperaturas.
Los pasos del esquema de búsqueda son:
- Acceder al primer elemento.
- Tratamiento inicial.
- Último elemento
- Actualizar encontrado
- Acceder al siguiente elemento.
- Tratamiento final.
Partes | Código |
---|---|
Acceso al primer elemento | i := 1; |
Tratamiento inicial | no hace falta inicializar ninguna variable; encontrado := false; |
Comprobar si ya hemos llegado al último elemento | while i <= 365 and not encontrado do |
Actualizar encontrado | t[i] := readReal(); if t[i] ≤ 0 entonces encontrado:= true; |
Acceso al siguiente elemento | if not encontrado then i:= i +1; end if end while |
Tratamiento final | writeBoolean(encontrado) |
Por lo tanto, este sería el esquema de búsqueda:
algorithm esquemaBusqueda
var
encontrado: boolean;
end var
{acceder_primer_elemento}
{tratamiento_inicial}
encontrado:= false;
while not {último elemento} and not encontrado do
if {elemento tratado cumple condición} then
encontrado:= true;
else
{acceder al siguiente elemento}
end if
end while
{tratamiento_final}
end algorithm
Tener en cuenta que:
- Recorremos únicamente la secuencia hasta encontrar el elemento que cumple una determinada condición.
- Se contempla la posibilidad de que no se encuentre el elemento buscado.
- El esquema finaliza si se encuentra el elemento buscado o si se ha recorrido toda la secuencia.