続・クイックソート
そういえば、同じ値がたくさん入っている列をソートするときに、先日の partition をつかった実装だと、再帰呼び出しのなかで再度おなじ値の比較が走ることになって比較コストがばかにならないなあ… とおもった。
strcmp のように、一回の比較で同じか、小さいか、それとも大きいかを決定できる場合、同じ値を再帰ソートにまわさないようにすれば効率が上がるだろう。 …ってんで、ちょいと書き直してみた。:
data Ordinal = LessThan | EqualsTo | GreaterThan qsort :: (a -> a -> Ordinal) -> [a] -> [a] qsort _ [] = [] qsort compare (x:xs) = qsort compare ls ++ (x:es) ++ qsort compare gs where (ls, es, gs) = partition3 compare x xs partition3 :: (a -> a -> Ordinal) -> a -> [a] -> ([a], [a], [a]) partition3 compare x vs = aux vs ([], [], []) where aux [] result = result aux (v:vs) (ls, es, gs) = case compare v x of LessThan -> aux vs (ls++[v], es, gs) EqualsTo -> aux vs (ls, es++[v], gs) GreaterThan -> aux vs (ls, es, gs++[v])
ここでたとえば次のような比較関数を定義して、…
compar :: Ord a => a -> a -> Ordinal compar x y = case (x < y, x <= y) of (True, _) -> LessThan (_, True) -> EqualsTo otherwise -> GreaterThan
先の qsort に渡すと、…
*Main> qsort compar [1,2,1,1,2] [1,1,1,2,2]
と、ソートができる。