ALGORITMOS DE ORDENAMIENTO COUNTING SORT CHRISTIAN ESTEBAN ALDANA ROZO BRAYAN STIF FORERO CRUZ GIOVANNY GUZMÁN CÉSPEDES JORGE MEJIA Profesora: DIANA MABEL DIAZ UNIVERSIDAD PILOTO DE COLOMBIA INGENIERIA DE SITEMAS ANALISIS Y DISEÑO DE ALGORTIMOS BOGOTA D.C. 2010 HISTORIA El algoritmo de ordenamiento Counting Sort (Ordenamiento por Cuentas en español) es un algoritmo de ordenamiento en el que se cuenta el número de elementos de cada clase para luego ordenarlos. Sólo puede ser utilizado por tanto para ordenar elementos que sean contables, por ejemplo, los números enteros de un determinado intervalo, sin contar números reales. El algoritmo fue creado por Harold H. Seward en 1954, ANÁLISIS DEL ALGORITMO El primer paso consiste en averiguar cuál es el intervalo dentro del que están los datos a ordenar (valores mínimo y máximo). Después se crea un vector de números enteros con tantos elementos como valores haya en el intervalo [mínimo, máximo], y a cada elemento se le da el valor 0 (0 apariciones). Tras esto se recorren todos los elementos a ordenar y se cuenta el número de apariciones de cada elemento (usando el vector que hemos creado). Por último, basta con recorrer este vector para tener todos los elementos ordenados. Se trata de un algoritmo estable cuya complejidad computacional es O(n+k), siendo n el número de elementos a ordenar y k el tamaño del vector auxiliar (máximo - mínimo). Si este último intervalo es muy amplio, el algoritmo es ineficiente, ya que el vector auxiliar tiene un tamaño excesivamente grande, lo que supondría un gran coste en memoria y también en tiempo. Con lenguajes de programación que no permitan definir vectores cuyo primer índice sea un valor distinto de 0 ó 1 es necesario realizar una traducción de los valores. Por ejemplo, si el intervalo es [4,10] y el vector auxiliar se define como vaux = vector[1..7], el valor 6 deberá incrementar el contador de la posición vaux[3].Si en lugar de esta simple traducción se utilizan funciones más complejas, entonces el algoritmo se denominaría bucket sort. Counting sort es un algoritmo de ordenamiento similar al algoritmo de Bucket sort, este algoritmo tiene la ventaja de conocer el rango de los números de la matriz a ordenar (matriz A). Utiliza este rango para crear una matriz C de esta longitud. Cada índice i en C matriz se utiliza para contar el número de elementos en una cuenta con el valor, y luego cuenta almacenada en C se puede utilizar para poner los elementos de A en su posición correcta en la matriz resultante ordenados. El algoritmo fue creado por Harold H. Seward en 1954. CORRECTITUD INICIO: En el desarrollo del algoritmo se tienen como datos de entrada números con un rango entre cero y k (0≤ Sn ≥ K), es decir que el primer paso del algoritmo será verificar si todos los elementos a ordenar son menores o iguales que k. El algoritmo también deberá encontrar el valor de Px quien es el que representa la cantidad de elementos de entrada que son menores o iguales a x (0≤ Sn ≥ K). Así por ejemplo si Psi es igual a K sabemos que podemos insertar a Si directamente en la posición K en el arreglo de salida, ya que entre los elementos que queremos ordenar hay exactamente K elementos menores que S1. MANTENIMIENTO (INVARIANTE) Cuidado especial se debe tener cuando el arreglo de entrada contiene elementos repetidos. FINALIZACIÓN Después que los datos de entrada han pasado por los diferentes ciclos de este algoritmo, finalizara cuando todos los valores de entrada estén en su posición correspondiente. En el primer ciclo sólo cuenta solo cuantos valores iguales a x existen y los almacena en un vector P[x]. En el segundo ciclo hace una suma acumulativa para que finalmente P[x] almacene la cantidad de valores que son menores o iguales a x. En el ciclo final se inserta cada elemento directamente en la posición que le corresponde. Si el valor x no se encuentra repetido en A, su posición final será directamente P[x]−1. Ahora si x se encuentra repetido en A su posición final dependerá de cuantos valores iguales a x se hayan insertado ya en el arreglo final, por esto después de insertar a x se decrementa el valor de P[x]. COMPLEJIDAD La primera observación importante del procedimiento Counting-Sort tiene que ver con su complejidad. El primer y último ciclo toman tiempo lineal con respecto a n, y el ciclo intermedio toma tiempo lineal con respecto a k, por lo que el tiempo total para countingsort es O(n +k). Esto nos dice que en una situación práctica en la que k sea O(n), countingsort tardará tiempo lineal con respecto a n, O(n) en el peor caso mejorando la cota (n log n). La segunda observación tiene que ver con esto ´ultimo, countingsort en ningún momento realiza comparaciones entre los elementos que se quieren ordenar por lo que la cota (n log n) no es válida para ´el. Como última observación alguien podría preguntarse por qué el último ciclo es desde n−1 a 0 y no desde 0 a n−1. De hecho, si el ciclo se realizara aumentando el contador, Counting-Sort seguiría ordenando los valores. El ciclo se hace decrementando el contador para darle a Counting-Sort la importante propiedad de ser estable. Un algoritmo de ordenación es estable cuando para cada par de elementos iguales, estos aparecen finalmente en el mismo orden en el que aparecían en el arreglo inicial. En el próximo algoritmo que estudiemos veremos la importancia de esta propiedad. ORDEN DE COMPLEJIDAD LINEA DE CODIGO COMPLEJIDAD Random rnd = new Random(); 1 rnd.nextInt(); 1 int[] A = new int[100000]; 1 for (long i=0; i < 1000000; i++) { n int n = n depende del número de iteraciones, (int)(rnd.nextDouble()*100000); que para este caso específico es A[n]++;} 1000000. for (long i=0; i < 1000000; i++) { n int n = n depende del número de iteraciones, (int)(rnd.nextDouble()*100000); que para este caso específico es A[n]++;} 1000000. int[] B = counting_sort(A,10000); 1 for(int i=0;i<B.length;i++){ n System.out.print(B[i]+" : "+i+" , "); } n depende de la longitud del vector, que para este caso específico es 10000. int[] C = new int[k+1]; 1 int[] B = new int[A.length]; n n depende de la longitud del vector, que para este caso específico es 10000. for(int i=0;i<=k;i++){ n C[i]=0; } n depende del número de iteraciones k, k es igual a la longitud del vector C. for(int j=0;j<A.length;j++){ n C[A[j]]+=1; } n depende de la longitud del vector, que para este caso específico es 100000. for(int i=1;i<=k;i++){ n C[i]+=C[i-1]; } n depende del número de iteraciones k, k es igual a la longitud del vector C. for(int j=A.length-1;j>=0;j--){ n B[C[A[j]]-1]=A[j]; n depende de la longitud del vector, que C[A[j]]-=1; } para este caso específico es 100000 return B; 1 Tiempo de Ejecución n=10 n=100 n=1000 n=10000 n=100000 En conclusión podemos decir que el algoritmo es de tipo n2