2011-02-07 16 views
12

Voy a implementar una cinta de juguete "mainframe" para los estudiantes, mostrando la rapidez de las funciones de clase "quicksort" (recursivas o no, en realidad no importa, debido al hardware lento y las conocidas técnicas de inversión de pila) en comparación con la clase de función "bubblesort". Entonces, aunque tengo clara la implementación del hardware y los controladores, supuse que la función de conexión rápida es mucho más rápida que otras en términos de secuencia, orden y distancia de comparación (es mucho más rápido rebobinar la cinta desde el medio que desde el mismo fin, debido a la diferente velocidad de rebobinado).¿Por qué la función de envío rápido de C es mucho más lenta (comparaciones de cinta, intercambio de cintas) que la función de ordenación de burbujas?

Lamentablemente, esto no es cierto; Este simple código de "burbuja" muestra grandes mejoras en comparación con las funciones de "quicksort" en términos de distancias de comparación, dirección y número de comparaciones y escrituras.

así que tengo 3 preguntas:

  1. ¿Tiene Tengo un error en mi implememtation de la función de la clasificación rápida?
  2. ¿Tengo un error en la implementación de la función bubblesoft?
  3. Si no es así, ¿por qué la función "bubblesort" es mucho más rápida en operaciones de comparación y escritura que en la función "quicksort"?

ya tengo una función de "ordenación rápida":

void quicksort(float *a, long l, long r, const compare_function& compare) 
{ 
    long i=l, j=r, temp, m=(l+r)/2; 
    if (l == r) return; 
    if (l == r-1) 
    { 
     if (compare(a, l, r)) 
     { 
      swap(a, l, r); 
     } 
     return; 
    } 
    if (l < r-1) 
    { 
     while (1) 
     { 
      i = l; 
      j = r; 
      while (i < m && !compare(a, i, m)) i++; 
      while (m < j && !compare(a, m, j)) j--; 
      if (i >= j) 
      { 
       break; 
      } 
      swap(a, i, j); 
     } 
     if (l < m) quicksort(a, l, m, compare); 
     if (m < r) quicksort(a, m, r, compare); 
     return; 
    } 
} 

y tengo mi propia implementación de la función "BubbleSort":

void bubblesort(float *a, long l, long r, const compare_function& compare) 
{ 
    long i, j, k; 
    if (l == r) 
    { 
     return; 
    } 
    if (l == r-1) 
    { 
     if (compare(a, l, r)) 
     { 
      swap(a, l, r); 
     } 
     return; 
    } 
    if (l < r-1) 
    { 
     while(l < r) 
     { 
      i = l; 
      j = l; 
      while (i < r) 
      { 
       i++; 
       if (!compare(a, j, i)) 
       { 
        continue; 
       } 
       j = i; 
      } 
      if (l < j) 
      { 
       swap(a, l, j); 
      } 
      l++; 
      i = r; 
      k = r; 
      while(l < i) 
      { 
       i--; 
       if (!compare(a, i, k)) 
       { 
        continue; 
       } 
       k = i; 
      } 
      if (k < r) 
      { 
       swap(a, k, r); 
      } 
      r--; 
     } 
     return; 
    } 
} 

he utilizado estas funciones de clasificación en una Código de ejemplo de prueba, como este:

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <conio.h> 

long swap_count; 
long compare_count; 

typedef long (*compare_function)(float *, long, long); 
typedef void (*sort_function)(float *, long , long , const compare_function&); 

void init(float *, long); 
void print(float *, long); 

void sort(float *, long, const sort_function&); 
void swap(float *a, long l, long r); 

long less(float *a, long l, long r); 
long greater(float *a, long l, long r); 

void bubblesort(float *, long , long , const compare_function&); 
void quicksort(float *, long , long , const compare_function&); 

void main() 
{ 
    int n; 
    printf("n="); 

    scanf("%d",&n); 
    printf("\r\n"); 

    long i; 
    float *a = (float *)malloc(n*n*sizeof(float)); 

    sort(a, n, &bubblesort); 
    print(a, n); 

    sort(a, n, &quicksort); 
    print(a, n); 

    free(a); 
} 

long less(float *a, long l, long r) 
{ 
    compare_count++; 
    return *(a+l) < *(a+r) ? 1 : 0; 
} 

long greater(float *a, long l, long r) 
{ 
    compare_count++; 
    return *(a+l) > *(a+r) ? 1 : 0; 
} 

void swap(float *a, long l, long r) 
{ 
    swap_count++; 

    float temp; 

    temp = *(a+l); 
    *(a+l) = *(a+r); 
    *(a+r) = temp; 
} 

float tg(float x) 
{ 
    return tan(x); 
} 

float ctg(float x) 
{ 
    return 1.0/tan(x); 
} 

void init(float *m,long n) 
{ 
    long i,j; 
    for (i = 0; i < n; i++) 
    { 
     for (j=0; j< n; j++) 
     { 
      m[i + j*n] = tg(0.2*(i+1)) + ctg(0.3*(j+1)); 
     } 
    } 
} 

void print(float *m, long n) 
{ 
    long i, j; 
    for(i = 0; i < n; i++) 
    { 
     for(j = 0; j < n; j++) 
     { 
      printf(" %5.1f", m[i + j*n]); 
     } 
     printf("\r\n"); 
    } 
    printf("\r\n"); 
} 

void sort(float *a, long n, const sort_function& sort) 
{ 
    long i, sort_compare = 0, sort_swap = 0; 

    init(a,n); 

    for(i = 0; i < n*n; i+=n) 
    { 
     if (fmod (i/n, 2) == 0) 
     { 
      compare_count = 0; 

      swap_count = 0; 
      sort(a, i, i+n-1, &less); 

      if (swap_count == 0) 
      { 
       compare_count = 0; 
       sort(a, i, i+n-1, &greater); 
      } 

      sort_compare += compare_count; 
      sort_swap += swap_count; 
     } 
    } 

    printf("compare=%ld\r\n", sort_compare); 
    printf("swap=%ld\r\n", sort_swap); 

    printf("\r\n"); 
} 
+4

Nota: todas las complejidades se otorgan a algunas funciones de "costo". Quicksort es O (n log n) en promedio, donde 'n' determina el número de comparaciones. Esto no se ha elegido arbitrariamente, en el caso del cálculo "regular" este es un buen indicador del costo. Sin embargo, cuando se trata de un dispositivo no convencional (aquí una cinta), sería más preciso calcular la complejidad en términos de "movimientos" de la cinta. Creo que es una excelente manera de hacer que sus alumnos piensen en lo que es "complejidad". –

+0

He encontrado que en realidad mi algo es bastante parecido al algoritmo de ordenación de selección en la media de los intercambios. Se encuentra aquí: http://www.sorting-algorithms.com/selection-sort, si está interesado puede encontrar allí una explicación muy interesante, dónde usar un alogrithm particular y sus ventajas y desventajas particulares. –

Respuesta

32

Creo que t El problema es que la mayoría de las implementaciones de quicksort dependen de un paso de partición que alterna las lecturas y escrituras en los extremos opuestos de la región que se va a ordenar. En un modelo de acceso aleatorio esto está perfectamente bien (todas las lecturas son esencialmente O (1)), pero en una cinta esto puede ser extremadamente costoso, ya que intercambiar entre los diferentes extremos del rango que se va a clasificar puede tomar O (n) tiempo a medida que el carrete de cinta rueda hacia adelante y hacia atrás. Esto convierte lo que normalmente es un paso de particionamiento de O (n) en algo que es potencialmente O (n), que domina el tiempo de ejecución de la función. Además, dado que el tiempo requerido para realizar una búsqueda de cinta es probablemente miles o millones de veces más lento que la frecuencia del procesador, este O (n) tiene un factor constante enorme.

La ordenación de burbuja, por otro lado, no tiene este problema porque siempre compara celdas adyacentes en la matriz. Hace como máximo O (n) pasa sobre la matriz y, por lo tanto, requiere que la cinta se rebobine solo n veces. La lógica de procesamiento es definitivamente más costosa en el ordenamiento de burbujas, más que cualquier otra O (n) ordenar, pero esto no es nada comparado con el tiempo ahorrado al no buscar la cinta hacia adelante y hacia atrás.

En resumen, el quicksort debería funcionar mucho más despacio en una cinta que en la clase de burbuja simplemente porque requiere que la cinta se mueva mucho más durante la ejecución. Dado que la búsqueda de cintas es costosa, la ventaja de tiempo de ejecución natural de quicksort se va a comer en este paso, y la burbuja de burbujas debería verse mucho más rápido.

+0

+1 No esperaba ver esta respuesta completa y completa tan rápidamente;) Phanx;) ¡Lo pensaré! –

11

templatetypedef's answer está justo en el dinero.Los accesos de bubblesort no solo están distribuidos mínimamente, sino que opera in situ. Sospecho que en realidad es el mejor algoritmo de clasificación para una máquina que tiene una sola cinta arbitrariamente lenta y solo O (1) RAM. [EDIT:. De hecho cocktail sort (una versión bidireccional de BubbleSort) debe ser mejor, ya que evita rebobinados despilfarro - gracias Steve Jessop]

Si tiene 4 unidades de cinta disponibles entonces mergesort rules the roost. Con solo 3 cintas, se puede usar a fancier version of mergesort.

+1

Creo que las condiciones para que el tipo de burbuja sea óptimo son incluso más específicas que eso, algo así como los tambores de memoria de 2 cabezas. –

+0

@Steve: Suena interesante, aunque no sé qué son los "tambores de memoria de 2 cabezas" :) Ahora me pregunto qué algoritmo de ordenamiento sería mejor que bubblesort para el modelo que describí (1 cinta lenta + O (1) RAM): todo lo que se me ocurre necesita más memoria o acceso aleatorio rápido. Ideas? –

+1

http://en.wikipedia.org/wiki/Drum_memory, tenga en cuenta que "algunas tiendas de tambores como el Univac FASTRAND tenían una o más cabezas móviles". Con una cinta lenta IIRC puedes hacer mejor que sortear burbujas haciendo una cosa similar con un paso de burbuja en una dirección, luego otra burbuja volverá a pasar, para no tener que rebobinar la cinta costosamente para volver al comienzo como tú sería en una especie de burbuja pura. Posiblemente llamado "tipo de cóctel"? –

1

Una de las razones por las que QuickSort es más rápido que el tipo de burbuja es que mueve elementos instantáneamente a grandes distancias. Si QuickSort mueve un elemento hasta 50 elementos, luego baja 20, sube 10, sube 5 y baja 2 antes de que termine en su lugar correcto, el elemento terminará en 43 ranuras desde donde comenzó, mientras que solo se movió 5 veces. La clasificación de burbuja habría movido el elemento 43 veces. Si mover el elemento de una ranura cuesta lo mismo que moverlo 50, esa es una gran victoria. Sin embargo, si el costo de mover un elemento es proporcional a la distancia, QuickSort habrá movido el elemento una distancia total de 87 ranuras, el doble que el tipo de burbuja.

Si uno está atascado tratando con unidades de cinta, el algoritmo óptimo dependerá mucho de cómo funcionen físicamente las unidades. Por ejemplo, en algunas unidades, las únicas operaciones son rebobinar y prepararse para la escritura (borrando efectivamente la cinta en el proceso), rebobinar y prepararse para la lectura, y procesar el siguiente byte (leer o escribir, dependiendo del modo de rebobinado). Otras unidades permiten el acceso aleatorio de bloques individuales y su reemplazo en cualquier parte de la cinta. Algunas unidades están limitadas a leer en una dirección. Otros (por ejemplo, cintas QIC) tienen algunas pistas que se leen en una dirección y otras que se leen en la otra dirección. No sé si alguna unidad permitiría leer o escribir el mismo bloque de datos en ambas direcciones, pero eso sería al menos teóricamente posible.

Cuestiones relacionadas