繁体   English   中英

List.fold和foldBack的渐近时间复杂度

[英]Asymptotic time complexity of List.fold and foldBack

我试图理解这两个函数的时间复杂度。 我试过试验这两个,这就是我想出来的

List.foldBack (@) [[1];[2];[3];[4]] [] => [1] @ List.foldBack (@) [[2];[3];[4]] []
=> [1] @ ([2] @ List.foldBack (@) [[3];[4]] [])
=> [1] @ ([2] @ ([3] @ List.foldBack (@) [4] []))
=> [1] @ ([2]@([3] @ ([4] @ List.foldBack[])))
=> [1]@([2]@([3]@([4]@([])))
=> [1; 2; 3; 4]


List.fold (@) [] [[1];[2];[3];[4]]
=> List.fold (@) (([],[1])@ [2]) [[3];[4]]
=> List.fold (@)  ((([]@[1])@[2])@[3]) [[4]]
=> List.fold (@)  (((([]@[1])@[2])@[3])@[4]) []
=> (((([]@[1])@[2])@[3])@[4])

现在在我看来它们都是线性的,因为它需要相同的计算量来实现相同的结果。 我是正确还是有些东西我错过了?

如果每个内部操作是Θ(1),则List.foldList.foldBack是O(n),其中n是列表的长度。

但是,要估计渐近时间复杂度,您需要依赖Θ(1)运算。 在你的例子中,事情有点微妙。

假设您需要连接每个列表包含m元素的n列表。 由于@是左操作数长度的O(n) ,因此我们有foldBack复杂性:

  m + ... + m // n occurences of m
= O(m*n)

fold

  0 + m + 2*m + ... + (n-1)*m // each time length of left operand increases by m
= m*n*(n-1)/2
= O(m*n^2)

因此,使用@天真方式, foldBack是线性的,而fold是输入列表大小的二次方。

值得注意的是, @是关联的(a @(b @ c)=(a @ b)@ c); 因此,在这种情况下, foldfoldBack结果是相同的。

实际上,如果内部运算符是非关联的,我们需要通过使用foldfoldBack来选择正确的顺序。 并且通过将列表转换为数组,使F#中的List.foldBack成为尾递归; 这个操作也有一些开销。

List.foldList.foldBack函数都是对其函数参数的T( n )调用,其中n是列表的长度。 但是,您传递的是(@)函数,它不是T(1)而是T( m ),其中m是第一个参数列表的长度。

特别是,这个:

(((([]@[1])@[2])@[3])@[4])

T(N²)因为[1]@[2]是一个操作,然后[1;2]@[3]是两个操作,然后[1;2;3]@[4]是三个操作。

在一个天真的实现中, FoldBackO(n^2)因为你需要继续遍历列表。 在F#编译器实际上创建了一个临时数组,并反转,然后调用Fold ,所以时间复杂性(在以下方面O )为O(n)两个,但Fold将由恒定量稍快

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM