Curso-lenguaje-C/fundamentos-programacion/PR4/README.md
2024-06-23 20:26:43 +02:00

11 KiB

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. Aproximació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:

  1. 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.
  2. Tratamiento inicial. Inicializamos las variables que utilizaremos para hacer el tratamiento posterior.
  3. Último elemento. Comprobamos si hemos llegado al último elemento.
  4. Trater elemento. Aplicamos las acciones necesarias para resolver cada uno de los datos a tratar.
  5. Acceso al siguiente elemento.
  6. 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:

  1. Acceder al primer elemento.
  2. Tratamiento inicial.
  3. Último elemento
  4. Actualizar encontrado
  5. Acceder al siguiente elemento.
  6. 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.

Volver arriba

Volver a la PR3 | Seguir a la PEC 6