2008-11-12 8 views
8

Tengo que ordenar una serie de enteros, que pueden tener valores entre 30.000.000 y 350.000.000. Habrá entre 0 y 65.535 enteros, con un recuento promedio de 20.000. El uso de RAM es irrelevante y la velocidad solo es importante.¿Cuál es el algoritmo de ordenación más rápido para enteros 0-65535?

Más adelante también tendré que dividirlos en grupos, siempre con la división siempre que la brecha entre dos de estos valores sea> 65.535, que es para lo que necesito el algoritmo.

Si hace alguna diferencia, el algoritmo se usará en una secuencia de comandos de Perl.

Editar: Después de pensarlo y leer las respuestas me he dado cuenta de algo: en realidad no me importan los datos en sí. Como realmente solo quiero encontrar los valores de inicio y fin de grupos con espacios pequeños, la ordenación solo necesita crear divisiones y puede descartar los datos.

Edit2: Después de algunas pruebas y probar las respuestas proporcionadas, la manera más rápida que encontré fue esto:

my @sort = sort {$a <=> $b} @item_offsets; 
my @buckets; 
my $start = shift @sort; 
push @buckets, [$start,$start]; 
for my $item (@sort) { 
    if ($item < $buckets[$#buckets][1]+$gap) { 
     $buckets[$#buckets][1] = $item; 
    } 
    else { 
     push @buckets, [$item,$item]; 
    } 
} 
say $#buckets; 

Respuesta

17

que acababa de hacer una serie de cubos antes de ejecutar el algoritmo, una para cada grupo de 65536 valores consecutivos. Los depósitos contendrán un valor mínimo y máximo de sus contenidos, pero no almacenarán los contenidos ellos mismos. Después de ejecutar el algoritmo, haga una sola pasada sobre los cubos. Si hay dos cubos no vacíos consecutivos con min (bucket2) -max (bucket1) < 65536, combínelos. La combinación no ocurrirá hasta que el algoritmo termine de ejecutarse. Deseche los cubos vacíos. Este algoritmo es tiempo lineal.

Tome nota de Bucket Sort.

+0

Logró resumir los problemas realmente bien. De hecho, mientras leía las respuestas aquí, reflexioné sobre hacer algo así, pero aún no estaba muy seguro. Gracias. :) – Mithaldu

+0

Acabo de editar mi respuesta y descarté un poco de texto no relacionado, en función de sus ediciones. La respuesta resultante debería ser mucho más rápida, aunque ambos fueron algoritmos de tiempo lineal. – Brian

12

que haría uso de una especie radix, ya que hay que agrupar el resultado.

+2

Se puede encontrar un módulo de clasificación de raíz en CPAN @ http://search.cpan.org/dist/Sort-Radix/ – draegtun

5

Sólo iba a decir tipo de raíz, http://en.wikipedia.org/wiki/Radix_sort, sin embargo, eso podría ser un poco más de lo que buscaba implementar, Introsort es generalmente la solución de clasificación aceptada para datos http://en.wikipedia.org/wiki/Introsort, es una variación de quicksort que cambia a heapsort cuando alcanza conjuntos más pequeños, ya que es más rápido en juegos más pequeños que en el grupo rápido.

0

Si utiliza el número como índice de una matriz, y luego incrementa el recuento de esa posición, los ha "agrupado" y lo ha hecho de una vez.

en pseudocódigo:

while(morenumbers) 
    sorted[[unsorted[number]]++ 
    number++ 

Si el rango es conocido de antemano, se puede reducir el índice de los valores (por ejemplo, el valor-30000 para ponerla en el rango de la derecha).

+0

Mala idea, ya que el rango es mucho mayor que el número de enteros (50 millones frente a 65 mil), por lo que este "one pass" será muy lento. –

+1

No puede obtener más de una pasada, ya que debe presionar cada elemento de la lista desordenada al menos una vez en cualquier algoritmo de clasificación que exista. código de Perl se vería más como my @sorted_values; foreach my $ element (@unsorted_values) { $ sorted_values ​​[$ element] ++; }; –

+0

¡Aargh! ¡Puse saltos de línea para evitar que el código Perl se viera tan mal como una línea! –

17

Es poco probable que usted será capaz de escribir un algoritmo de ordenación en Perl que se obtienen mejores resultados que incorporado sort función de Perl:

Usted puede experimentar con el pragma tipo para ver si un algoritmo particular es mejor:

use sort '_quicksort'; 
use sort '_mergesort'; 

Desde sus puntos de corte pueden variar en función de la distribución de los datos, creo que es necesario para ordenar la lista entera primero y luego bucle sobre ella para hacer el corte.

my $prev = shift @numbers; # already sorted 
my @group = [$prev]; 
my $i  = 0; 

foreach my $n (@numbers) { 
    $i++ if ($n - $prev > 65535); 
    push @{$group[$i]}, $n; 
    $prev = $n; 
} 
+0

Gracias por recordarme la función de ordenamiento Perl. Me había olvidado de eso. – Mithaldu

1

me gustaría probar esto:

my @sorted = map { unpack "N" } sort map { pack "N" } @unsorted; 
+0

Me temo que el mapa es un poco de magia negra para mí. ¿Qué hace ese pedazo de código? oO – Mithaldu

+1

Supongo que el mapa {} es para eliminar la necesidad de un sortsub para obtener una comparación numérica. El caso de {$ a <=> $ b} se ha optimizado desde 5.6.1 por lo que el engaño no debería ser necesario. –

+0

Tienes que leer esto de derecha a izquierda. El mapa {paquete "N"} @unsorted aplica el paquete "N" a cada elemento - convirtiendo cada elemento en un número binario big-endian - el resultado se pasa al género que tiene un comportamiento O (n log n), y cada uno El elemento del resultado ordenado se desempaqueta y se asigna a @sorted. –

Cuestiones relacionadas