Analisis de Algoritmos Busqueda y Ordenacion

8
Analisis de algoritmos Metodo de ordenación Shell Denominado así por su desarrollador Donald Shell (1959), ordena una estructura de una manera similar a la del ordenamiento de burbuja, sin embargo no ordena elementos adyacentes sino que utiliza una segmentación entre los datos. Esta segmentación puede ser de cualquier tamaño de acuerdo a una secuencia de valores que empiezan con un valor grande (pero menor al tamaño total de la estructura) y van disminuyendo hasta llegar al '1'. Una secuencia que se ha comprobado ser de las mejores es: ...1093, 364, 121, 40, 13, 4, 1. En contraste, una secuencia que es mala porque no produce un ordenamiento muy eficiente es ...64, 32, 16, 8, 4, 2, 1. Su complejidad es de O(n 1.2 ) en el mejor caso y de O(n 1.25 ) en el caso promedio. void shellsort ( int a[], int n) { int x,i,j,inc,s; for(s=1; s < t; s++) /* recorre el arreglo de incrementos */ { inc = h[s]; for(i=inc+1; i < n; i++) { x = a[i]; j = i-inc; while( j > 0 && a[j] > x) { a[j+h] = a[j]; j = j-h; } a[j+h] = x; } } }

description

Analisis de Algoritmos Busqueda y Ordenacion y complejidad

Transcript of Analisis de Algoritmos Busqueda y Ordenacion

Page 1: Analisis de Algoritmos Busqueda y Ordenacion

Analisis de algoritmos

Metodo de ordenación Shell

Denominado así por su desarrollador Donald Shell (1959), ordena una estructura de una manera similar a la del ordenamiento de burbuja, sin embargo no ordena elementos adyacentes sino que utiliza una segmentación entre los datos. Esta segmentación puede ser de cualquier tamaño de acuerdo a una secuencia de valores que empiezan con un valor grande (pero menor al tamaño total de la estructura) y van disminuyendo hasta llegar al '1'. Una secuencia que se ha comprobado ser de las mejores es: ...1093, 364, 121, 40, 13, 4, 1. En contraste, una secuencia que es mala porque no produce un ordenamiento muy eficiente es ...64, 32, 16, 8, 4, 2, 1.

Su complejidad es de O(n1.2) en el mejor caso y de O(n1.25) en el caso promedio.

void shellsort ( int a[], int n){

int x,i,j,inc,s;

for(s=1; s < t; s++) /* recorre el arreglo de incrementos */{

inc = h[s];for(i=inc+1; i < n; i++) {

x = a[i];j = i-inc;while( j > 0 && a[j] > x){

a[j+h] = a[j];j = j-h;

}a[j+h] = x;

}}

}

En todos los años que han pasado desde su publicación, mucha gente se ha dedicado al estudio de este algoritmo (ya lo comentábamos al principio). La secuencia original de saltos que propuso Donald Shell es la que hemos utilizado hasta ahora: empezamos con un salto k=n/2 (siendo n la longitud del array) y vamos dividiendo el salto sucesivamente por dos hasta finalizar con un salto k=1.

Un hecho curioso es que cualquier secuencia descendente de saltos hace que el algoritmo funcione también correctamente, siempre y cuando el último salto sea k=1. El hecho de terminar con k=1 hace que el algoritmo de Shell se comporte finalmente exactamente igual que el de Inserción directa, lo que garantiza que en última instancia, todo elemento llegue a su posición correcta.

Page 2: Analisis de Algoritmos Busqueda y Ordenacion

Sin embargo, esto nos plantea una nueva cuestión ¿Existe una secuencia de saltos que proporcione, en general, mejores resultados que la propuesta originalmente?...

Pues realmente sí: de hecho, la propuesta original ni siquiera es una de las mejores. Incluso, se ha podido demostrar que las secuencias geométricas (como ir dividiendo k por dos o por cualquier otro entero) no son especialmente buenas.

De esa manera, se ha podido llegar a otras secuencias que se comportan mejor. Ninguna de ellas es estrictamente geométrica.

El análisis teórico de la complejidad del algoritmo utilizando esas secuencias es extremadamente complicado -y no necesariamente útil-, así que muchas de esas secuencias han sido obtenidas experimentalmente. Comparar unas con otras también es tarea complicada... así que simplemente, adaptaremos el algoritmo para utilizar una cualquiera de las muchas que hay propuestas.

Nos vamos a quedar con una de las más conocidas. La secuencia de saltos propuesta por Robert Sedgewick es la siguiente:

{1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}

Lógicamente, debe terminar en 1. Empezaremos con el primer valor de la lista que sea menor que la longitud del array a ordenar... y seguiremos hasta llegar al 1.

Como ésta y otras muchas de estas secuencias no responden a una fórmula sencilla, lo mejor es utilizar un array para almacenar la secuencia. Haremos que k vaya tomando valores de ese array en sucesivas iteraciones del bucle exterior.

void ShellSort(int[] A)

{

//la secuencia de saltos de Sedgewick

//Puede ser sustituida por cualquier otra, siempre y

//cuando el último valor sea un 1

int[] gaps ={1391376, 463792, 198768, 86961, 33936, 13776,

4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1};

//utilizamos un índice para movernos por la secuencia.

//nos colocamos al principio.

int indiceGap = 0;

//el primer salto o gap (representado por la variable k)

//es el mayor valor del array gaps que sea menor que la

//longitud de nuestro array a

while (gaps[indiceGap] > A.Length)

{

Page 3: Analisis de Algoritmos Busqueda y Ordenacion

indiceGap++;

};

//este primer bucle nos mantendrá dentro del algoritmo

//mientras el salto sea mayor o igual que 1. Es decir,

//mientras no hayamos agotado los saltos del array gaps

while (indiceGap < gaps.Length)

{

//Tomamos el valor del salto en esta iteración

int k = gaps[indiceGap];

//Nos metemos en la k-ordenación

for (int i=k; i<A.Length; i++)

{

int v = A[i];

int j = i - k;

while (j >= 0 && A[j] > v)

{

A[j + k] = A[j];

j -= k;

}

A[j + k] = v;

} //fin for

//pasamos al siguiente valor de salto

indiceGap++;

} //fin while

} //ShellSort

Para comprobar que, en efecto, la secuencia de Sedgewick se comporta mejor que la original de Shell hemos hecho algunas pruebas. Hemos hecho pruebas con arrays de enteros rellenos de números aleatoriamente, y hemos pasado el algoritmo con la secuencia original y el que tiene la secuencia de Sedgewick, y hemos contado el número de intercambios que ha realizado cada uno. Los resultados han sido estos (redondeando a dos dígitos significativos para no marear):

Tamaño Intercambios Shell Intercambios Sedgewick

1000 8800 7500

5000 61000 53000

Page 4: Analisis de Algoritmos Busqueda y Ordenacion

10000 150000 120000

30000 520000 460000

100000 2700000 1700000

500000 18 millones 9 millones

1 millón 44 millones 17 millones

5 millones 270 millones 99 millones

10 millones 640 millones 220 millones

No es que demuestre nada, pero los resultados son bastante significativos.

El algoritmo SHELL es un algoritmo eficaz, relativamente eficiente en la mayoría de los casos, fácil de implementar, no consume memoria extra dinámicamente y se comporta bastante bien para unos datos de entrada de mediano tamaño.

Por último, todo esto está muy bien... pero yo he utilizado este algoritmo para ordenar enteros ¿Se puede utilizar para ordenar Objetos u otro tipo de datos?

Pues sí... sin ningún problema, pero hay que adaptarlo un poco. Primero, necesitamos tener un criterio claro de ordenación, y que algún método nos permita saber dados dos objetos cualesquiera si están en orden o no

Método de búsqueda binaria

La búsqueda binaria al igual que otros algoritmos como el quicksort utiliza la técnica divide y vencerás. Uno de los requisitos antes de ejecutar la búsqueda binaria, es que el conjunto de elementos debe de estar ordenado. Supongamos que tenemos el siguiente array.

57 53 21 37 17 36 22 3 44 97 89 26 31 47 8 17

Debemos ordenarlo3 8 17 17 21 22 26 31 36 37 44 47 53 57 89 97

¿Cómo funciona la búsqueda binaria?

Necesitamos una seria de datos para realizar la búsqueda: El elemento en la posición inicio, fin, medio. Y por supuesto el tamaño del vector y elemento que queremos buscar.

int first = 0;int middle = 0;int last = arraySize - 1;middle = (first + last) / 2;

Preguntamos si el elemento buscado es igual al elemento que se encuentra en la posición medio del array, si es afirmativo ya encontramos el elemento y no hay nada más que hacer.

Page 5: Analisis de Algoritmos Busqueda y Ordenacion

if (searched == array[middle]) { cout << "Se encuentra en la posición " << middle + 1 << endl; return array[middle];}

Si no es así. Preguntamos si el elemento que se encuentra en la posición medio es mayor al elemento buscado, si es afirmativo y como el array está ordenado, quiere decir que elemento buscado es menor al del medio. Entonces ahora solo buscaríamos en esa división del array,por lo tanto, el fin ahora sería el elemento anterior al medio.

if (array[middle] > searched) { last = middle - 1;}

De lo contrario quiere decir que elemento buscado es mayor al del medio, entonces debemos buscar en la otra división del array, por lo tanto el inicio sería el elemento posterior al medio.

else { first = middle + 1;}

Con cualquiera de estas 2 operaciones descartamos la otra mitad del array y por lo tanto reducimos notablemente el número de iteraciones.

Por lo tanto, en cada iteración, la búsqueda se reduce a la mitad del array. Realizamos esto en un bucle mientras el inicio sea menor o igual al fin.

El algoritmo de búsqueda binaria tiene una complejidad de O(log n).

Veamos la implementación de la búsqueda lineal y binaria en C++.

Aquí tienen el código del algoritmo y una breve explicación.

#include <iostream>#include <stdlib.h>#include "quicksort.cpp"

using namespace std;

int binary_search(int *array, int searched, int arraySize){ int first = 0; int middle; int last = arraySize - 1;

while (first <= last) { middle = (first + last) / 2;

if (searched == array[middle]) { cout << "Se encuentra en la posición " << middle + 1 << endl; return array[middle]; } else { if (array[middle] > searched) {

Page 6: Analisis de Algoritmos Busqueda y Ordenacion

last = middle - 1; } else { first = middle + 1; } } } return -1;}

void print(int *array, int arraySize){ for (int i = 0; i < arraySize; i++) { cout << array[i] << " "; } cout << endl << endl;}

int main(){ int arraySize; int searched; cout << "Ingresa el tamanyo del array" << endl; cin >> arraySize;

int array[arraySize];

srand(time(NULL)); for (int i = 0; i < arraySize; i++) { array[i] = rand() % 100; }

cout << endl; cout << "Array al inicio: " << endl; print(array, arraySize);

cout << "Array ordenado: " << endl; quicksort(array, 0, arraySize); print(array, arraySize);

cout << "Busqueda binaria -> Ingresa el elemento a buscar: "; cin >> searched; binary_search(array, searched, arraySize);}

Ejemplo de la salida del programa:

Ingresa el tamanyo del array16

Page 7: Analisis de Algoritmos Busqueda y Ordenacion

Array al inicio57 53 21 37 17 36 22 3 44 97 89 26 31 47 8 17

Array ordenado3 8 17 17 21 22 26 31 36 37 44 47 53 57 89 97

Ingresa el elemento a buscar (Busqueda binaria) 89El elemento se encuentra en la posicion 15