sorting - sort - ordenar enteros rĂ¡pidamente en haskell
transpose haskell (3)
Consideraría el uso de vectores en lugar de listas para esto, ya que las listas tienen una gran cantidad de elementos por cada sobre, mientras que un vector unboxed es esencialmente solo un bloque contiguo de bytes. El paquete de algoritmos de vectores contiene varios algoritmos de clasificación que puede usar para esto, incluido el ordenamiento de radix , que espero que le vaya bien en su caso.
Aquí hay un ejemplo simple, aunque podría ser una buena idea mantener el resultado en forma de vector si planea seguir procesándolo en él.
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Algorithms.Radix as R
sort :: [Int] -> [Int]
sort = V.toList . V.modify R.sort . V.fromList
Además, sospecho que una parte importante del tiempo de ejecución de su ejemplo proviene del generador de números aleatorios, ya que el estándar no se conoce exactamente por su rendimiento. Debes asegurarte de estar cronometrando solo la parte de clasificación, y si necesitas muchos números aleatorios en tu programa, hay generadores más rápidos disponibles en Hackage.
¿Hay alguna función en las bibliotecas de Haskell que ordena enteros en O (n) tiempo? [Por, O (n) quiero decir más rápido que el género de comparación y específico para los enteros]
Básicamente encuentro que el siguiente código lleva mucho tiempo con el género (en comparación con sumar la lista sin ordenar):
import System.Random
import Control.DeepSeq
import Data.List (sort)
genlist gen = id $!! sort $!! take (2^22) ((randoms gen)::[Int])
main = do
gen <- newStdGen
putStrLn $ show $ sum $ genlist gen
La suma de una lista no requiere deepseq, pero lo que intento hacer sí, pero el código anterior es lo suficientemente bueno para los punteros que estoy buscando.
Tiempo: 6 segundos (sin ordenar); aproximadamente 35 segundos (con clasificación)
Memoria: alrededor de 80 MB (sin clasificación); alrededor de 310 MB (con clasificación)
Nota 1: la memoria es un problema mayor que el tiempo para mí aquí, en cuanto a la tarea actual, estoy recibiendo errores de memoria (el uso de memoria se convierte en 3 GB después de 30 minutos de tiempo de ejecución)
Supongo que los algoritmos más rápidos también proporcionarán una mejor impresión de la memoria del apostador, por lo tanto, buscarán el tiempo O (n).
Nota 2: Estoy buscando algoritmos rápidos para Int64, aunque los algoritmos rápidos para otros tipos específicos también serán útiles.
Solución utilizada: IntroSort con vectores sin caja fue lo suficientemente bueno para mi tarea:
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Algorithms.Intro as I
sort :: [Int] -> [Int]
sort = V.toList . V.modify I.sort . V.fromList
Esto está tomado del libro de Richard Bird, Pearls of Functional Algorithm Design (aunque tuve que editarlo un poco, ya que el código del libro no compilaba exactamente como estaba escrito).
import Data.Array(Array,accumArray,assocs)
sort :: [Int] -> [Int]
sort xs = concat [replicate k x | (x,k) <- assocs count]
where count :: Array Int Int
count = accumArray (+) 0 range (zip xs (repeat 1))
range = (0, maximum xs)
Funciona al crear una matriz indexada por enteros donde los valores son la cantidad de veces que aparece cada entero en la lista. Luego crea una lista de los índices, repitiéndolos la misma cantidad de veces que ocurrieron en la lista original de acuerdo con los recuentos.
Debe tener en cuenta que es lineal con el valor máximo en la lista, no la longitud de la lista, por lo que una lista como [ 2^x | x <- [0..n] ]
[ 2^x | x <- [0..n] ]
no se ordenaría de forma lineal.
La idea de ordenar los números usando una matriz es la correcta para reducir el uso de memoria.
Sin embargo, usar el máximo y el mínimo de la lista como límites puede causar que se exceda el uso de la memoria o incluso un error en el tiempo de ejecución cuando maximum xs - minimum xs > (maxBound :: Int)
.
Por lo tanto, sugiero que escriba los contenidos de la lista en una matriz mutable sin caja, clasificándola in situ (p. Ej., Con quicksort) y luego compilando una lista a partir de esa.
import System.Random
import Control.DeepSeq
import Data.Array.Base (unsafeRead, unsafeWrite)
import Data.Array.ST
import Control.Monad.ST
myqsort :: STUArray s Int Int -> Int -> Int -> ST s ()
myqsort a lo hi
| lo < hi = do
let lscan p h i
| i < h = do
v <- unsafeRead a i
if p < v then return i else lscan p h (i+1)
| otherwise = return i
rscan p l i
| l < i = do
v <- unsafeRead a i
if v < p then return i else rscan p l (i-1)
| otherwise = return i
swap i j = do
v <- unsafeRead a i
unsafeRead a j >>= unsafeWrite a i
unsafeWrite a j v
sloop p l h
| l < h = do
l1 <- lscan p h l
h1 <- rscan p l1 h
if (l1 < h1) then (swap l1 h1 >> sloop p l1 h1) else return l1
| otherwise = return l
piv <- unsafeRead a hi
i <- sloop piv lo hi
swap i hi
myqsort a lo (i-1)
myqsort a (i+1) hi
| otherwise = return ()
genlist gen = runST $ do
arr <- newListArray (0,2^22-1) $ take (2^22) (randoms gen)
myqsort arr 0 (2^22-1)
let collect acc 0 = do
v <- unsafeRead arr 0
return (v:acc)
collect acc i = do
v <- unsafeRead arr i
collect (v:acc) (i-1)
collect [] (2^22-1)
main = do
gen <- newStdGen
putStrLn $ show $ sum $ genlist gen
es razonablemente rápido y usa menos memoria. Aún utiliza mucha memoria para la lista, 2 22 Int
s toman 32 MB de almacenamiento en bruto (con Int
64 bits), con una sobrecarga de lista de iirc de cinco palabras por elemento, que suma hasta 200MB, pero menos de la mitad del original.