映射和过滤器看起来像是线性O(n),因为它们只需要遍历列表一次,但它们的复杂性是否受传递函数的影响?例如下面两个相同顺序的例子?
map (+) list
map (complex_function) list
答案 0 :(得分:5)
你似乎有一个(常见的)误解,即复杂性是n
的函数。
什么是n
?
n
只是衡量输入内容的一个参数。它可能不足以(或甚至是必要的)统计数据来描述您的输入 - 您可能需要其他变量来准确描述输入的复杂性。
因此,map
和filter
在n
中是线性。它们对其他变量的依赖取决于你传递的函数,但它们对n
的依赖通常不会。
(脚注:是的,你可以将一个函数传递给map
和filter
,因为它处理更多元素实际上执行了更多的工作,但那是无趣的除了我试图在这里做点之外。)
答案 1 :(得分:5)
在几乎所有情况下,当高阶函数的文档声明其复杂度为O(f(n))
时,这假设高阶函数具有恒定的时间复杂度O(1)
。此外,n
的确切含义可能会有所不同,但如果没有明确说明,则应从上下文中明确说明:列表的长度,集合/映射中的元素/关联的数量,等等。
假设我们有一个名为g
的更高阶函数g h ...
,其中h
是一个函数,而...
是一阶数据。如果没有关于高阶函数g
的任何其他信息,如果文档指出它为O(f(n))
,则可以获得O(f(n)) * CostH
更真实的最坏情况界限CostH
代表H
调用CostH
一次的费用。
当然,h
还取决于哪些参数开始传递给O(f(n))
:在这里,我们所知道的是这些参数是在h
时间构建的。很难对h
的参数大小有一个有用的一般约束,因为,例如,如果foo :: Int -> Tree ()
foo 0 = Tree []
foo m = Tree [t,t] where t = foo (m-1)
将树作为输入,那么你可以在短时间内构建一个非常大的树: / p>
2^m
以上内容在m
时间内构建了一个3^m
个叶子的树(感谢分享)。这可以适应b^m
或b*m
轻微保持g :: (a -> Int) -> [a] -> Int
复杂性。
但是,可能有一些方法可以利用参数化来恢复更有用的界限。例如,如果我们的订单功能具有类型
g h [...]
然后我们知道 h
只能使用从列表中获取的参数调用sortBy h [...]
。同样,库函数调用h
只能使用(function(){
})();
来比较提供列表中的两个元素。
但我不知道如何形式化和证明上述草拟的权利要求。很可能有一些关于这个主题的研究论文。
答案 2 :(得分:3)
有关复杂性和高阶函数的一些背景知识,请参阅,例如
公牛。符号逻辑3(1997),没有。 4,469--486。 http://projecteuclid.org/euclid.bsl/1182353537