理解涉及列表追加的递归SML函数的运行时(使用@)

时间:2012-11-29 01:09:02

标签: algorithm sml

我是算法分析和SML的新手,并且在以下SML函数的平均情况运行时挂起了。我很感激我的一些反馈。

fun app([]) = []
  | app(h::t) = [h] @ app(t)

因此,在每次递归之后,我们将得到一堆单个元素列表(以及一个无元素列表)。

[1]@[2]@[3]@...@[n]@[]

其中n是原始列表中元素的数量,而1, 2, 3, ..., n只是为了说明我们所讨论的原始列表中的元素。 L @ R在列表L的长度上需要时间线性。假设A是每个元素占用的恒定时间,我想象如下:

[1,2]@[3]@[4]@...@[n]@[] took 1A
[1,2,3]@[4]@...@[n]@[]   took 2A
[1,2,3,4]@...@[n]@[]     took 3A
...
[1,2,3,4,...,n]@[]       took (n-1)A
[1,2,3,4,...,n]          took nA

因此,我认为当时的再现看起来像这样:

T(0) = C                 (if n = 0)
T(n) = T(n-1) + An + B   (if n > 0)

其中C只是基本案例app([])B的最终匹配,是h::t的常量。关闭重复,我们将得到这个(证明省略):

T(n) = (n²+n)A/2 + Bn + C = (A/2)n² + (A/2)n + Bn + C = Θ(n²)

这是我自己的结论,与我提出的答案不同,即:

T(0) = B                 (if n = 0)
T(n) = T(n-1) + A        (if n > 0)

封闭表格

T(n) = An + B = Θ(n)

这是完全不同的。 (Θ(n)vsΘ(n²)!)但这不是假设L @ R需要恒定时间而不是线性?例如,添加

就是如此
fun add([]) = 0
  | add(h::t) = h + add(t) (* n + ... + 2 + 1 + 0 *)

甚至连接

fun con([]) = []
  | con(h::t) = h::con(t)  (* n :: ... :: 2 :: 1 :: [] *)

我是否误解L @ R存在的方式或我的分析(至少是那种)是正确的?

1 个答案:

答案 0 :(得分:1)

是。手动运行app [1,2,3]命令一次一个函数调用给出:

app [1,2,3]
[1]@(app [2,3])
[1]@([2]@(app [3]))
[1]@([2]@([3]@(app [])))
[1]@([2]@([3]@([])))
[1]@([2]@[3])
[1]@([2,3])
[1,2,3]

这是函数调用位于@左侧的结果。

将此与rev的朴素版本进行比较:

fun rev [] = []
  | rev (x::xs) = rev xs @ [x]

这个有你期望的运行时间:一旦递归完全扩展到表达式((([])@[3])@[2])@[1](采用线性时间),它需要n +(n - 1)+(n - 2)+ .. 。+ 1,或n(n + 1)/ 2,或O(n ^ 2)步骤完成计算。更有效的rev可能如下所示:

local
  fun rev' [] ys = ys
    | rev' (x::xs) ys = rev' xs (x::ys)
in
  fun rev xs = rev' xs []
end