miércoles, 13 de julio de 2011

Algoritmos de busqueda secuencial y binaria

INTRODUCCION
La búsqueda es una operación que tiene como objeto la localización de un elemento dentro de la estructura de datos con la que estamos trabajando.
Un algoritmo de búsqueda es una técnica para encontrar un valor dentro de la estructura, determinado como campo clave o llave.
La búsqueda termina.- con éxito, si encuentra la llave dentro de la estructura, sin éxito, pues no encuentra la llave dentro de la estructura.
El método de búsqueda a aplicar depende de si la lista esta ordenada o desordenada.
En base a esto, los algoritmos de búsqueda se clasifican en:
  • Internos.- aquellos que aplican a estructuras de datos como arreglos, tablas, listas ,etc.
  • Externos.- aquellos que aplican a archivos que se encuentran en dispositivos electrónicos.

También se pueden clasificar de acuerdo a si los datos se encuentran en orden o desordenados:
  • Secuencial o Lineal
  • Binaria o directa
BUSQUEDA SECUENCIAL
Es la forma mas simple de los métodos de búsqueda.
Consiste en reconocer y examinar cada uno de los elementos de la Lista o Arreglo, hasta encontrar el o los elementos buscados, o hasta haber recorrido todos los elementos de la lista.
Se define como el proceso de determinar el elemento, o su posición, que cumple con una condición; comparando con cada uno de los elementos en forma secuencial.
Se recomienda utilizar cuando el arreglo o lista no esta ordenado.

 

CODIFICACION EN C++:

1 int busq_lineal(int N, int array[],int elemento)
2 {    
3      int i,j;
4      for(i=j=0;i<N;i++)
5      {    
6           if(array[i]==elemento)
7           {    
8               return(i)
9           }
10     }
11     return(-1)
12 }

BUSQUEDA BINARIA
Es el método mas eficiente de búsqueda en una lista ordenada.
Básicamente el valor se compara con el elemento intermedio de la lista.
Sin son iguales, la búsqueda termina exitosamente; en caso contrario, debe buscarse en la mitad superior o inferior de la lista, repitiendo el proceso anterior.
Si recorre la lista y no ubica el valor, entonces no tiene éxito.

CODIFICACION EN C++:


1 int busqbin(int n,int a[],int valor)
2 {
3    int izq=1,der=n-1,medio;   
4    while(izq<=der)
5    {
6         medio=(izq+der)/2;
7         if(valor==a[medio])
8         {
9               return(medio);
10        }
11        if(valor>a[medio])
12        {
13                   izq=medio+1;
14        }
15        else
16        {
17            der=medio-1;
18        }
19   }
20   return(-1);
21 }
 
Fuente:
http://ronnyml.wordpress.com/2009/07/09/busqueda-lineal-y-busqueda-binaria/
http://www.algoritmia.net/articles.php?id=32

lunes, 11 de julio de 2011

Algoritmos de ordenamiento Parte 2 (Quicksort, Shell, Mezcla, Monticulo)

RAPIDO O QUICKSORT

  • Se le llama de Intercambio por partición.
  • Se define como un proceso recursivo.
  • Se basa en la técnica «divide y vencerás»
  • Se basa en el método de burbuja
Descripcion

Consiste en dividir la lista original en dos listas más pequeñas.
·         El consumo de recursos (memoria) es menor.
·         Mayor eficiencia y velocidad de procesamiento.
·         Posibilidad de dividir el trabajo en varios procesadores.

Se elige un elemento cualquiera (aunque en general se suele utilizar el que se encuentra en medio de la lista) que nos servirá como pivote.
Luego se recorre toda la lista, con el objeto de colocar los elementos más pequeños que el pivote a la izquierda del mismo, y los mayores a la derecha.
Las implementaciones más eficientes realizan esta tarea a la vez, recorriendo simultáneamente la lista en ambas direcciones e intercambiando entre sí cada par de elementos “descolocados” que se encuentran a su paso.
Culminada esta etapa tenemos un grupo de elementos menores que el pivote, el pivote, y otro grupo compuesto por números mayores a él.
En este punto es donde juega un papel importante la recursividad: si cada uno de esos grupos vuelve a dividirse en dos y se aplica lo explicado anteriormente de forma recursiva, tenemos resuelto el problema. Lo mejor de todo es que el tamaño de las sublistas -y por ende el tiempo que se necesita para procesarlas- es cada vez menor. 
En cada nivel el tamaño de las sublistas la mitad del anterior, lo que permite ordenar una lista de 1024 elementos en 10 “pasadas”, y una de más de un millón en solo 20.
Este algoritmo es fuertemente dependiente de la “suerte” que hayamos tenido al elegir el elemento pivote.
Si somos tan burros como para elegir un elemento que se encuentre en uno de los extremos de la lista, habremos dividido el problema en dos partes muy dispares (una con solo un elemento, ya ordenada, y otra casi del mismo tamaño que la original, desordenada) y el algoritmo no será tan eficiente.


Programa en C

1 int pivotar (int v[], int izq, int der) 
2 {
3           int posicionPivote = (izq + der) / 2;     
4           int valorPivote = v[posicionPivote];     
5           int indiceAlmacenamiento;     
6           swap (v, posicionPivote, der);//Se situal pivote al final del vector  
7            indiceAlmacenamiento = izq;      
8            for (int indiceLectura = izq; indiceLectura < der; indiceLectura++)
9           {         
10                   if (v[indiceLectura] <= valorPivote)
11                 {            
12                      swap (v, indiceLectura, indiceAlmacenamiento); 
13                      indiceAlmacenamiento++;        
14                 }     
15        }       
16        swap (v, indiceAlmacenamiento, der); //Se coloca el pivote en su lugar.      
17        return indiceAlmacenamiento; 
18 }   
 
19 void quicksort (int v[], int izq, int der) 
20 {    
21        int pivote;      
22        if(izq < der)
23       {         
24                 pivote = pivotar (v, izq, der); //Esta operación coloca el pivote en su lugar.  
25                 quicksort(v, izq, pivote-1);         
26                 quicksort(v, pivote+1, der);     
27       }  
28}

A continuacion les muestro un video que encontre en youtuube, en el cual se muestra una danza que ejemplifica el procedimiento del quicksort.


Análisis de eficiencia
  • Tiene aparente propiedad de trabajar mejor para elementos desordenados completamente, que para elementos semiordenados.
  • En promedio para todos los elementos, el método hace O(nlogn) comparaciones, el cual indica que es eficiente.
  • Para listas grandes consume mas memoria.
  • Se considera el método mas eficiente de los algoritmos.

SHELL


  • Versión mejorada de Inserción.
  • Se le conoce como de inserción con decrementos (incrementos decrecientes).
  • Es una generalización de inserción tomando en cuenta que:
  • El ordenamiento por inserción es eficiente si la lista esta casi ordenada.
  • El ordenamiento por inserción es ineficiente porque mueve los valores una posición a la vez.
Descripcion
  • Compara elementos no contiguos, y separados a una gran distancia.
  • Si los elementos no están en orden se intercambian.
  • Comienza especificando un salto, comparando elementos separados por dicho salto y se intercambian si es necesario.
  • Se divide el intervalo por dos y se repite el proceso.
  • Al finalizar el recorrido del arreglo, el salto es de uno y la ordenación funciona como burbuja.

Ejemplo:

Para el arreglo a = [6, 1, 5, 2, 3, 4, 0]
Tenemos el siguiente recorrido:

Recorrido
Salto
Lista Ordenada
Intercambio
1
3
2,1,4,0,3,5,6
(6,2), (5,4), (6,0)
2
3
0,1,4,2,3,5,6
(2,0)
3
3
0,1,4,2,3,5,6
Ninguno
4
1
0,1,2,3,4,5,6
(4,2), (4,3)
5
1
0,1,2,3,4,5,6
Ninguno

Codigo en C:

1 void shell_sort(int A[], int size)
2 { 
3         int i, j, incrmnt, temp;    
4         incrmnt = size/2;  
5         while (incrmnt > 0)  
6         {    
7                    for (i=incrmnt; i < size; i++)    
8                    {      
9                           j = i;       temp = A[i];      
10                         while ((j >= incrmnt) && (A[j-incrmnt] > temp))      
11                         {         A[j] = A[j - incrmnt];        
12                                    j = j - incrmnt;
13                         }     
14                         A[j] = temp;    
15                  }    
16                  incrmnt /= 2;  
17        }
18 }

A continuacion les muestro un video que encontre en youtuube, en el cual se muestra una danza que ejemplifica el procedimiento del metodo shell.


Análisis de eficiencia
  • Los requerimientos reales de tiempo para un ordenamiento especifico depende del numero de elementos en la lista y de sus valores reales.
  • Se ha demostrado que el orden de Shell sort puede aproximarse a O(n(logn) 2) si se usa una secuencia adecuada de incrementos.

MEZCLA



  • Se le llama también método de intercalación por fusión.
  • Llamado Merge Sort.
  • Es un algoritmo recursivo con un mínimo de comparaciones.
  • Aplicación clásica de la estrategia para resolución de algoritmos «divide y vencerás».
Descripcion
  • Dividir el arreglo en dos listas y ordenar cada una por separado.
  • Cuando están ordenadas, se pueden ir mezclando para así generar la lista ordenada original mas fácilmente.
  • La fusión de arreglos permite un método de ordenación rápido y potente.

Programa en C

1 void mezclar(int arreglo1[], int n1, int arreglo2[], int n2, int arreglo3[]) 
2 {     
3         int x1=0, x2=0, x3=0;       
4         while (x1<n1 && x2<n2) 
5        {         
6                if (arreglo1[x1]<arreglo2[x2]) 
7                {             
8                      arreglo3[x3] = arreglo1[x1];
9                      x1++;
10              } 
11              else 
12              { 
13                     arreglo3[x3] = arreglo2[x2];
14                     x2++;
15              }
16              x3++;
17      }    
18      while (x1<n1)
19      {
20               arreglo3[x3] = arreglo1[x1];
21               x1++;
22               x3++;
23      }
24      while (x2<n2)
25      {  
26               arreglo3[x3] = arreglo2[x2]; 
27               x2++;
28               x3++;
29      } 
30}   
31 void mezcla(int vector[], int n) 
31 {     
32        int *vector1, *vector2, n1, n2,x,y;     
33        if (n>1)     
34       { 
35              if (n%2 == 0)
36                n1=n2=(int) n / 2;
37              else 
38              {             
39                     n1=(int) n / 2;n2=n1+1;
40              }
41              vector1=(int *) malloc(sizeof(int)*n1);
42              vector2=(int *) malloc(sizeof(int)*n2);
43              for(x=0;x<n1;x++)
44                  vector1[x]=vector[x];
45              for(y=0;y<n2;x++,y++){ 
46                   vector2[y]=vector[x];
47                   mezcla(vector1, n1);
48                   mezcla(vector2,n2);
49                   mezclar(vector1, n1, vector2, n2, vector);
50                   free(vector1);
51                   free(vector2);}
52            }
53       }
54 int main()
55 {     
56        int i, vector[] = {2,3,5,7,2,6,1,5,8,3,2};
57        mezcla(vector,12);
58        for(i=0;i<12;i++)
59         printf("%i,\n", vector[i]);
60       return 0; 
61}

A continuacion les muestro un video que encontre en youtuube, en el cual se muestra una danza que ejemplifica el procedimiento del mezcla.


Análisis de eficiencia
  • Trabaja con una lista auxiliar, lo cual consume memoria y obviamente trabajo extra al copiar las listas.

MONTICULO


  • Algoritmo de ordenación recursivo.
  • Es no estable.
  • Su complejidad es de O(nlogn).
  • Se basa en una propiedad de los montículos, en la que, la cima contiene el menor elemento (o el mayor) de todos los almacenados en el.
  • Heap.- significa cola de prioridades.
Descripcion
  • Consiste en almacenar todos los elementos de la lista en un heap (árbol), y luego extraer el nodo que queda como raíz del árbol (cima).
  • Se realiza en sucesivas iteraciones, obteniendo así la lista ordenada.
  • Se mapea un árbol binario de tal forma en el arreglo que el nodo en la posición i es el padre de los nodos en las posiciones (2*i) y (2*i+1).
  • El valor de un nodo es mayor o igual a los valores de sus hijos. El nodo padre tiene el valor mayor de todo su subárbol .
Procedimiento:
  • Convertir la lista en un árbol.
  • Construir una lista ordenada de atrás hacia adelante (mayor a menor) haciendo lo siguiente:
  • Sacar el valor máximo en el árbol (el de la posición 1).
  • Poner el valor en la lista ordenada.
  • Reconstruir el árbol con un elemento menos.
  • Usar el mismo arreglo para el árbol y la lista ordenada.


Pseudocodigo

function heapsort(array A[0..n]):
  montículo M
  integer i := 124578
  for i = 0..n:
    insertar_en_monticulo(M, A[i])
  for i = 0..n:
    A[i] = extraer_cima_del_monticulo(M)
return A

A continuacion les muestro un video que encontre en youtuube, en el cual se muestra una danza que ejemplifica el procedimiento del heapsort o monticulo.



Análisis de eficiencia
  • En el caso promedio, el heapsort no es tan eficiente como el qucksort.
  • El heapsort requiere 2 veces mas tiempo que el quicksort.
  • No es muy eficiente para pocos elementos, por la creacion del árbol y el calculo de la ubicación de padres e hijos.
  • Requerimientos.- de una sola variable para guardar el valor de intercambio.


Fuente: 
Presentacion en PowerPoint "Estructuras: Algoritmos de ordenamiento y busqueda". Autor: M.C. Blanca I. Martnez C.
Investigacion en Word "Algoritmos de busqueda y ordenamiento".