Я хочу взять n самых больших элементов из ленивого списка.
Я слышал, что сортировка слиянием, реализованная в Data.List.sort, является ленивой и не создает больше элементов, чем необходимо. Это может быть правдой с точки зрения сравнений, но определенно не так, когда речь идет об использовании памяти. Следующая программа иллюстрирует проблему:
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import qualified Data.Heap as Heap
import qualified Data.List as List
import System.Random.MWC
import qualified Data.Vector.Unboxed as Vec
import System.Environment
limitSortL n xs = take n (List.sort xs)
limitSortH n xs = List.unfoldr Heap.uncons (List.foldl' (\ acc x -> Heap.take n (Heap.insert x acc) ) Heap.empty xs)
main = do
st <- create
rxs :: [Int] <- Vec.toList `fmap` uniformVector st (10^7)
args <- getArgs
case args of
["LIST"] -> print (limitSortL 20 rxs)
["HEAP"] -> print (limitSortH 20 rxs)
return ()
Время выполнения:
Data.List:
./lazyTest LIST +RTS -s [-9223371438221280004,-9223369283422017686,-9223368296903201811,-9223365203042113783,-9223364809100004863,-9223363058932210878,-9223362160334234021,-9223359019266180408,-9223358851531436915,-9223345045262962114,-9223343191568060219,-9223342956514809662,-9223341125508040302,-9223340661319591967,-9223337771462470186,-9223336010230770808,-9223331570472117335,-9223329558935830150,-9223329536207787831,-9223328937489459283] 2,059,921,192 bytes allocated in the heap 2,248,105,704 bytes copied during GC 552,350,688 bytes maximum residency (5 sample(s)) 3,390,456 bytes maximum slop 1168 MB total memory in use (0 MB lost due to fragmentation) Generation 0: 3772 collections, 0 parallel, 1.44s, 1.48s elapsed Generation 1: 5 collections, 0 parallel, 0.90s, 1.13s elapsed INIT time 0.00s ( 0.00s elapsed) MUT time 0.82s ( 0.84s elapsed) GC time 2.34s ( 2.61s elapsed) EXIT time 0.00s ( 0.00s elapsed) Total time 3.16s ( 3.45s elapsed) %GC time 74.1% (75.7% elapsed) Alloc rate 2,522,515,156 bytes per MUT second Productivity 25.9% of total user, 23.7% of total elapsed
Данные.Heap:
./lazyTest HEAP +RTS -s [-9223371438221280004,-9223369283422017686,-9223368296903201811,-9223365203042113783,-9223364809100004863,-9223363058932210878,-9223362160334234021,-9223359019266180408,-9223358851531436915,-9223345045262962114,-9223343191568060219,-9223342956514809662,-9223341125508040302,-9223340661319591967,-9223337771462470186,-9223336010230770808,-9223331570472117335,-9223329558935830150,-9223329536207787831,-9223328937489459283] 177,559,536,928 bytes allocated in the heap 237,093,320 bytes copied during GC 80,031,376 bytes maximum residency (2 sample(s)) 745,368 bytes maximum slop 78 MB total memory in use (0 MB lost due to fragmentation) Generation 0: 338539 collections, 0 parallel, 1.24s, 1.31s elapsed Generation 1: 2 collections, 0 parallel, 0.00s, 0.00s elapsed INIT time 0.00s ( 0.00s elapsed) MUT time 35.24s ( 35.46s elapsed) GC time 1.24s ( 1.31s elapsed) EXIT time 0.00s ( 0.00s elapsed) Total time 36.48s ( 36.77s elapsed) %GC time 3.4% (3.6% elapsed) Alloc rate 5,038,907,812 bytes per MUT second Productivity 96.6% of total user, 95.8% of total elapsed
Очевидно, что limitSortL намного быстрее, но он также очень требует памяти. В больших списках он попадает в размер ОЗУ.
Есть ли более быстрый алгоритм для решения этой проблемы, который не требовал бы памяти?
Изменить: пояснение: я использую Data.Heap из пакета кучи, я не пробовал пакет кучи.
n
, который вы ввели в первой строке, то есть размер выходного списка. - person Robin Green   schedule 10.05.2011