HERM/ORDENA.C
2021-09-12 20:06:36 +02:00

379 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Este programa forma parte del Curso de C
Copyright (C) 1991 Grupo Editorial Jackson
Todos los derechos reservados
*/
/* ORDENA: comparaci¢n entre varias tcnicas de ordenaci¢n */
/* Este programa se linka con la librer¡a de consola de Jackson, utilizando
el fichero ORDENA.PRJ para Turbo C 2.0, u ORDENA.MAK para Quick C 2.0.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "Jconio.h"
/* prototipos de funciones: */
void ExtraeDatos(double d[], int n);
void MuestraDatos(double d[], int n);
void Seleccion(double d[], int n);
void Insercion(double d[], int n);
void BubbleSort(double d[], int n);
void ShellSort(double d[], int n);
void Quicksort(double d[], int n);
int Compara(double *d1, double *d2);
void PulseReturn(void);
/********************************************************************
* main
********************************************************************/
main()
{
enum { NDATOS = 200 }; /*n£mero datos a reordenar*/
enum { F1 = 1059, F2, F3, F4, F5, F6 }; /*c¢digos teclas <1>*/
double datos[NDATOS]; /*datos a reordenar <2>*/
clock_t inicio,fin; /*tiempo inicial, final <3>*/
char buf[80];
int c,ok,terminado;
srand( (unsigned int)time(NULL)); /*inicia random*/
Jcursor(0); /*elimina cursor*/
do {
Jclrscr(); /*borra pantalla*/
Jclrkey(); /*y buffer teclado*/
Jputs("\r\n\n\n\n- Comparaci¢n entre tcnicas de ordenaci¢n -\r\n\n"
" F1) Ordenaci¢n por selecci¢n\r\n"
" F2) Ordenaci¢n por inserci¢n\r\n"
" F3) Burbuja\r\n"
" F4) Shell\r\n"
" F5) Quicksort\r\n"
" F6) Salir\r\n\n"
"Seleccione F1..F6: ");
do { /*lee tecla v lida*/
c = Jgetkey(1);
ok = (c >= F1 && c <= F6); /*(est n en orden) <4>*/
if (! ok) Jputch('\a');
} while (! ok);
terminado = (c == F6);
if (! terminado) {
ExtraeDatos(datos,NDATOS); /*datos a reordenar*/
Jclrscr(); /*los imprime*/
Jputs("Extracci¢n aleatoria de datos para ordenar:\r\n\n");
MuestraDatos(datos,NDATOS);
Jclrscr();
inicio = clock(); /*tiempo inicial*/
if (c == F1) { /*ejecuta ord. elegida*/
Seleccion(datos, NDATOS);
} else if (c == F2) {
Insercion(datos,NDATOS);
} else if (c == F3) {
BubbleSort(datos,NDATOS);
} else if (c == F4) {
ShellSort(datos,NDATOS);
} else if (c == F5) {
Quicksort(datos,NDATOS);
}
fin = clock(); /*tiempo final <5>*/
sprintf(buf,"ejecutado en %3.2f segundos.\r\n\n",
((double)(fin-inicio))/CLK_TCK);
Jputs(buf);
MuestraDatos(datos,NDATOS); /*imprime datos en orden*/
}
} while (! terminado);
Jclrscr(); /*borra pantalla*/
Jcursor(1); /*muestra cursor*/
}
/* Notas sobre main:
<1> Como los c¢digos de las teclas indicadas son n£meros progresivos,
basta con asignar el primero. enum se ocupa despus autom ticamente
de asignar valores crecientes (1060,1061,...) a las siguientes
constantes.
<2> Utilizamos un array de double porque la reordenaci¢n de un array int
es demasiado r pida. Las comparaciones y asignaciones de double dan
una mejor idea del comportamiento t¡pico en condiciones reales.
<3> Utilizamos clock en vez de time para obtener una mayor resoluci¢n
(cerca de 1/18.2 segundos en los PC). La funci¢n time tiene, sin
embargo, y siempre en los IBM PC y compatibles, una resoluci¢n
t¡pica de un segundo.
<4> Funciona porque los c¢digos de las teclas F1..F6 son n£meros enteros
progresivos (ver nota 1).
<5> Dividiendo el tiempo empleado para la constante CLK_TCK (definida en
<time.h>), se obtiene el tiempo en segundos. Recuerde el uso de
(double) para asegurar que la divisi¢n se ejecute en double, evitando
el riesgo de una divisi¢n entera que perder¡a la parte fraccionaria
(dependiendo de como est definido CLK_TCK, es mejor no arriesgarse
y utilizar double).
*/
/********************************************************************
* ExtraeDatos: extrae n enteros casuales en el array d[].
********************************************************************/
void ExtraeDatos(double d[], int n)
{
int i;
for (i = 0; i < n; i++) { /*en cada elemento*/
d[i] = rand()/100.0; /*un double casual <1>*/
}
}
/* Notas sobre ExtraeDatos:
<1> Pone dos cifras decimales s¢lo con fines estticos: los valores de
los datos no interesan, basta que vengan ordenados correctamente.
*/
/********************************************************************
* MuestraDatos: muestra los n enteros contenidos en el array d[].
********************************************************************/
void MuestraDatos(double d[], int n)
{
int i;
char buf[80];
for (i = 0; i < n; i++) { /*por cada elemento*/
sprintf(buf,"%7.2f",d[i]); /*imprime en 7 espacios*/
if(Jposx() > 72) { /*a sig. l¡n. si no est */
Jputs("\r\n");
}
Jputs(buf); /*muestra dato*/
}
Jputs("\r\n");
PulseReturn(); /*espera un Return*/
}
/********************************************************************
* Seleccion: reordena por selecci¢n el array d[] de n elementos.
********************************************************************/
void Seleccion(double d[], int n) /*<1>*/
{
int i,j,imin;
double temp;
Jputs("Ordenaci¢n por selecci¢n en curso...");
for (i = 0; i < n; i++) { /*para cada n£mero*/
imin = i; /*¡ndice m¡nimo de i <2>*/
for (j = i+1; j < n; j++) {
if (d[j] < d[imin]) { /*recuerda ¡ndice m¡nimo*/
imin = j;
}
}
temp = d[i]; /*intercamb. con m¡n. <3>*/
d[i] = d[imin];
d[imin] = temp;
}
}
/* Notas sobre Selecci¢n:
<1> A partir del primer elemento del array, se selecciona el elemento de
valor m s bajo y se coloca en primer lugar, intercambi ndolo con el
que estaba en primer lugar; despus se avanza i al segundo elemento,
se busca el nuevo m¡nimo y se intercambia con el segundo, etctera.
En la pr ctica, se seleccionan los elementos uno cada vez en orden
creciente, y se colocan a partir del inicio del array.
<2> imin es el ¡ndice del elemento de valor m s bajo entre los que ya hay
para ordenar. Para buscar el elemento de valor m s bajo, se supone
que es el primero (imin = i). Si despus encuentra uno m s bajo, se
cambia imin de forma que contenga el ¡ndice del elemento m s bajo.
<3> Para el intercambio es necesaria una variable intermedia: el C no
tiene instrucciones para intercambiar dos variables, y una funci¢n
ser¡a demasiado ineficaz.
*/
/********************************************************************
* Insercion: reordena por inserci¢n el array d[] de n elementos.
********************************************************************/
void Insercion(double d[], int n) /*<1>*/
{
int i,j;
double z;
Jputs("Ordenaci¢n por inserci¢n en curso...");
for (i = 1; i < n; i++) {
z = d[i]; /*n£m. a insertar <2>*/
j = i; /*busca hacia atr s*/
while (j > 0 && d[j-1] > z) { /*<3>*/
d[j] = d[j-1]; /*hace sitio <4>*/
j--;
}
d[j] = z; /*inserta*/
}
}
/* Notas sobre Inserci¢n:
<1> Cada elemento se inserta en el lugar adecuado entre los ya
ordenados. Al principio se considera ordenado s¢lo el primer elemento,
por lo que se parte del segundo. Cada elemento se inserta como
cuando se ordena una mano de cartas de una baraja.
<2> z es el valor a insertar en el lugar adecuado entre los anteriores.
<3> Vuelve hacia atr s entre los elementos ya ordenados hasta que no
encuentra uno menor o igual a z. z puede insertarse por lo tanto
justo despus del elemento encontrado. Recuerde que si la primera
condici¢n (j > 0) no es cierta, la segunda ni siquiera se prueba,
evitando as¡ acceder a un elemento inexistente del array d[].
<4> Mientras va hacia atr s, coloca delante los elementos aprovechando
el lugar en el que estaba z. De este modo, cuando encuentra el lugar
adecuado s¢lo debe escribir en l z (el contenido original ya se ha
colocado delante).
*/
/********************************************************************
* BubbleSort: reordena por intercambios el array d[] de n elementos.
********************************************************************/
void BubbleSort(double d[], int n) /*<1>*/
{
int i,intercambio;
double temp;
Jputs("Ordenaci¢n por burbuja en curso...");
do {
intercambio = 0;
for (i = 1; i < n; i++) { /*explora por pares*/
if (d[i] < d[i-1]) {
temp = d[i];
d[i] = d[i-1];
d[i-1] = temp;
intercambio = 1; /*e indica <2>*/
}
}
} while (intercambio); /*sale si no intercambia*/
}
/* Notas sobre BubbleSort:
<1> A partir del primer par de elementos, el array se explora comparando
pares de elementos adyacentes. Si no est n bien ordenados, se
intercambian entre ellos. Se repite el procedimiento hasta que en
una pasada no se realiza ning£n intercambio, porque los elementos
est n todos en orden.
<2> El flag (variable l¢gica) intercambio sirve para indicar que se ha
realizado al menos un intercambio, y por ello se debe dar otra pasada.
*/
/********************************************************************
* ShellSort: reordena por Shell sort el array d[] de n elementos.
********************************************************************/
void ShellSort(double d[], int n) /*<1>*/
{
int dist; /*distancia entre los elementos a ordenar en una pasada*/
int i,j;
double z;
Jputs("Ordenaci¢n Shell en curso...");
for (dist = 1; dist < n; dist = dist*3+1) /*distancia inicial <2>*/
;
while ( (dist = dist/3) > 0) { /*hasta distancia 1*/
for (i = dist; i < n; i++) { /*<3>*/
z = d[i]; /*n£m. a insertar*/
j = i; /*busca hacia atr s*/
while (j-dist >= 0 && d[j-dist] > z) {
d[j] = d[j-dist]; /*hace sitio*/
j -= dist;
}
d[j] = z; /*inserta*/
}
}
}
/* Notas sobre ShellSort:
<1> Se realiza una ordenaci¢n por inserci¢n considerando s¢lo un elemento
cada dist elementos, partiendo con dist bastante grande. Esto coloca
r pidamente hacia los extremos los elementos que tienen que moverse
mucho para lograr colocarse en su lugar adecuado. dist se reduce
despus gradualmente hasta 1, es decir, a una ordenaci¢n por
inserci¢n, que sin embargo es rapid¡sima, porque los elementos est n
ya en sus lugares correctos.
<2> Una secuencia particularmente eficaz para dist es (al revs) la
serie 1, 4, 13, 40, 121, ... (multiplica por tres y a¤ade uno).
Observe que todo el trabajo se hace en la misma for: la instrucci¢n
dependiente de for es una instrucci¢n vac¡a.
<3> El ciclo interno es una ordenaci¢n normal por inserci¢n, s¢lo que se
considera £nicamente un elemento cada dist.
*/
/********************************************************************
* Quicksort: reordena con Quicksort el array d[] de n elementos.
********************************************************************/
void Quicksort(double d[], int n)
{
Jputs("Ordenaci¢n Quicksort en curso...");
qsort(d,n,sizeof(d[0]),
(int(*)(const void*, const void*)) Compara); /*<1>*/
}
/***** Compara: compara dos elementos, por Quicksort. *****/
int Compara(double *d1, double *d2)
{
if (*d1 > *d2) return 1; /*<2>*/
else if (*d1 < *d2) return -1;
else return 0;
}
/* Notas sobre Quicksort:
<1> En este complejo programa se declara que Compara es un puntero a una
funci¢n que recibe dos punteros a void y devuelve un int. Es necesario
con los compiladores 100% ANSI porque la funci¢n de librer¡a qsort no
puede conocer con anticipaci¢n el tipo exacto de los argumentos que
debe pasarle a la funci¢n de comparaci¢n, y por ello los declara como
punteros a void. El programa finje que los argumentos son del tipo
deseado por qsort, aunque en realidad sean de otro tipo (en este caso,
se trata de punteros a double).
Obviamente, hacindolo de esta forma se impide al compilador que
controle la exactitud de la llamada; por ello es necesario prestar
mucha atenci¢n. Como alternativa, se podr¡a modificar la declaraci¢n
de Compara utilizando void* y efectuando en su interior los pasos de
void* a double*.
<2> No se puede hacer simplemente *d1-*d2, porque el valor devuelto por
la funci¢n debe ser un int: se perder¡a la parte fraccionaria y
n£meros como 12.47 y 12.34 (distintos s¢lo en su parte fraccionaria)
ser¡an considerados iguales. De este modo, entre otras cosas, la
ejecuci¢n es m s r pida. Se aceptan tres return, ya que dadas las
exiguas dimensiones de la funci¢n, no hay problemas de legibilidad.
*/
/********************************************************************
* PulseReturn: espera una tecla Return.
********************************************************************/
void PulseReturn(void)
{
Jclrkey();
Jputs("\r\nPulse Return para continuar: ");
while (Jgetkey(1) != '\r') {
Jputch('\a');
}
}