First commit 11/11/1991

This commit is contained in:
2021-09-12 20:06:36 +02:00
commit 8fe25e5bc5
58 changed files with 51891 additions and 0 deletions

379
ORDENA.C Normal file
View File

@ -0,0 +1,379 @@
/*
Este programa forma parte del Curso de C
Copyright (C) 1991 Grupo Editorial Jackson
Todos los derechos reservados
*/
/* ORDENA: comparaci<63>n entre varias t<>cnicas de ordenaci<63>n */
/* Este programa se linka con la librer<65>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<63>n entre t<>cnicas de ordenaci<63>n -\r\n\n"
" F1) Ordenaci<63>n por selecci<63>n\r\n"
" F2) Ordenaci<63>n por inserci<63>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<73>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<EFBFBD>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 despu<70>s autom<6F>ticamente
de asignar valores crecientes (1060,1061,...) a las siguientes
constantes.
<2> Utilizamos un array de double porque la reordenaci<63>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<63>n
(cerca de 1/18.2 segundos en los PC). La funci<63>n time tiene, sin
embargo, y siempre en los IBM PC y compatibles, una resoluci<63>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<73>n se ejecute en double, evitando
el riesgo de una divisi<73>n entera que perder<65>a la parte fraccionaria
(dependiendo de como est<73> 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 est<73>ticos: 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<73>*/
Jputs("\r\n");
}
Jputs(buf); /*muestra dato*/
}
Jputs("\r\n");
PulseReturn(); /*espera un Return*/
}
/********************************************************************
* Seleccion: reordena por selecci<63>n el array d[] de n elementos.
********************************************************************/
void Seleccion(double d[], int n) /*<1>*/
{
int i,j,imin;
double temp;
Jputs("Ordenaci<EFBFBD>n por selecci<63>n en curso...");
for (i = 0; i < n; i++) { /*para cada n<>mero*/
imin = i; /*<2A>ndice m<>nimo de i <2>*/
for (j = i+1; j < n; j++) {
if (d[j] < d[imin]) { /*recuerda <20>ndice m<>nimo*/
imin = j;
}
}
temp = d[i]; /*intercamb. con m<>n. <3>*/
d[i] = d[imin];
d[imin] = temp;
}
}
/* Notas sobre Selecci<63>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<62>ndolo con el
que estaba en primer lugar; despu<70>s se avanza i al segundo elemento,
se busca el nuevo m<>nimo y se intercambia con el segundo, etc<74>tera.
En la pr<70>ctica, se seleccionan los elementos uno cada vez en orden
creciente, y se colocan a partir del inicio del array.
<2> imin es el <20>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 despu<70>s encuentra uno m<>s bajo, se
cambia imin de forma que contenga el <20>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<63>n
ser<65>a demasiado ineficaz.
*/
/********************************************************************
* Insercion: reordena por inserci<63>n el array d[] de n elementos.
********************************************************************/
void Insercion(double d[], int n) /*<1>*/
{
int i,j;
double z;
Jputs("Ordenaci<EFBFBD>n por inserci<63>n en curso...");
for (i = 1; i < n; i++) {
z = d[i]; /*n<>m. a insertar <2>*/
j = i; /*busca hacia atr<74>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<63>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<74>s entre los elementos ya ordenados hasta que no
encuentra uno menor o igual a z. z puede insertarse por lo tanto
justo despu<70>s del elemento encontrado. Recuerde que si la primera
condici<63>n (j > 0) no es cierta, la segunda ni siquiera se prueba,
evitando as<61> acceder a un elemento inexistente del array d[].
<4> Mientras va hacia atr<74>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 <20>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<EFBFBD>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<73>n bien ordenados, se
intercambian entre ellos. Se repite el procedimiento hasta que en
una pasada no se realiza ning<6E>n intercambio, porque los elementos
est<73>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<EFBFBD>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<74>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<63>n por inserci<63>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
despu<70>s gradualmente hasta 1, es decir, a una ordenaci<63>n por
inserci<63>n, que sin embargo es rapid<69>sima, porque los elementos est<73>n
ya en sus lugares correctos.
<2> Una secuencia particularmente eficaz para dist es (al rev<65>s) 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<63>n
dependiente de for es una instrucci<63>n vac<61>a.
<3> El ciclo interno es una ordenaci<63>n normal por inserci<63>n, s<>lo que se
considera <20>nicamente un elemento cada dist.
*/
/********************************************************************
* Quicksort: reordena con Quicksort el array d[] de n elementos.
********************************************************************/
void Quicksort(double d[], int n)
{
Jputs("Ordenaci<EFBFBD>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<63>n que recibe dos punteros a void y devuelve un int. Es necesario
con los compiladores 100% ANSI porque la funci<63>n de librer<65>a qsort no
puede conocer con anticipaci<63>n el tipo exacto de los argumentos que
debe pasarle a la funci<63>n de comparaci<63>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, haci<63>ndolo de esta forma se impide al compilador que
controle la exactitud de la llamada; por ello es necesario prestar
mucha atenci<63>n. Como alternativa, se podr<64>a modificar la declaraci<63>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<63>n debe ser un int: se perder<65>a la parte fraccionaria y
n<>meros como 12.47 y 12.34 (distintos s<>lo en su parte fraccionaria)
ser<65>an considerados iguales. De este modo, entre otras cosas, la
ejecuci<63>n es m<>s r<>pida. Se aceptan tres return, ya que dadas las
exiguas dimensiones de la funci<63>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');
}
}