Haskell中sortBy的复杂性

时间:2013-05-26 15:20:12

标签: algorithm haskell complexity-theory big-o

我想出了一个idea来解决另一个SO question,我希望能帮助确定函数的复杂性,因为我对此并不太了解。我是否正确地猜测每个块的“未分类”将是O (n * log 2)?然后sortBy函数的复杂性是什么,它将一个块的最后一个与另一个块的头部进行比较?我的猜测是,该函数只会比较对,而不需要根据总列表找到一个块的顺序。另外,由于整体功能的懒惰优化,Haskell会提供不同的复杂性吗?提前谢谢!

import Data.List.Split (chunksOf)
import Data.List (sortBy)

rearrange :: [Int] -> [Int]
rearrange = concat 
          . sortBy (\a b -> compare (last a) (head b)) 
          . map (sortBy (\a b -> compare b a)) 
          . chunksOf 2

2 个答案:

答案 0 :(得分:5)

让我们孤立地看一下这些部分(让n成为list参数的长度):

  • chunksOf 2O(n),生成长度为(n+1) `quot` 2的列表。
  • map (sortBy ...):由于传递给sortBy ...的所有列表的长度都为<= 2,因此每个排序都为O(1),因此整个map为再次O(n)
  • sortBy (\a b -> compare (last a) (head b)):比较始终为O(1),因为采用last元素的列表具有有限长度(<= 2),因此整个sortBy } operation是O(n*log n)
  • concat再次O(n)

总的来说,我们有O(n*log n)

但请注意

cmp = \a b -> compare (last a) (head b)

是一个不一致的比较,对于两个列表ab(比如[30,10][25,15]),您可以

cmp a b == cmp b a = LT

我不确定您的算法是否始终有效。

在查看sortBy的实现并在我的脑海中稍微跟踪排序之后,我认为对于给定的目的,它可行(如果列表元素是不同的)并且不一致的比较没有坏处。对于某些排序算法,不一致的比较可能导致排序循环,但对于合并排序变体,不应该发生。

答案 1 :(得分:4)

一步一步

  1. chunksOf 2必须遍历整个列表,所以O(n)和我们列表长度的一半。但是,由于常数倍数不会影响复杂性,我们可以忽略它。
  2. map (sortBy...遍历整个列表O(n)执行常量操作* O(1) = O(1*n) = O(n)
  3. sortByO( n * log n)
  4. 进行恒定时间比较*
  5. concatO(n)
  6. 总共O(n + n + n log n + n) = O ((3 + log n) * n) = O(n log n)

    *由于列表的保证长度为2或更短,我们可以说排序和访问最后一个元素的操作分别是O(2 * log 2)O(2),它们都是常量时间{ {1}}