递归函数与循环的复杂性

时间:2012-06-06 05:07:31

标签: algorithm recursion ocaml complexity-theory

我有一个在列表上工作的递归函数,该函数包含一个调用自身的循环,最后是另一个函数g。它的结构类似如下,为了简化问题,我们可以假设l总是一个没有重复元素的列表。

let rec f l = function
  | [] -> g ()
  | _ ->
      List.fold_left
        (fun acc x ->
           let lr = List.filter (fun a -> (a <> x)) l in
           acc + (f lr))
        1 l

我不确定如何使用List.length l以及g的复杂性来表达此功能的复杂性。

我认为它与g的复杂性和List.length l阶乘成正比,是否有人可以确认?

2 个答案:

答案 0 :(得分:1)

好。我并不是说看起来很好斗。这看起来确实像函数式编程作业,因为它不是非常实用的代码。

令F(n)为比较次数加上长度为n的输入的加法次数。让G成为g的运行时间。由于g不接受任何操作数,因此G是常数。我们只计算它所调用的次数。

折叠将执行其功能n次。每次执行都会调用filter进行n次比较,每次从输入中删除一个元素,然后在这个缩短的列表上递归调用f并进行一次加法。所以总费用是

F(n)= n *(n + F(n-1)+ 1)[如果n> 0]  = G [否则]

第一个词扩展为

F(n)= n * F(n-1)+ n ^ 2 + n

这是你提出的O(n!+ n ^ 3 + n ^ 2 + nG)= O(n!+ nG)。

我希望这有用。

答案 1 :(得分:1)

由于您假设列表l不包含任何重复项,因此该函数所做的是计算所有子列表,这些子列表的元素少于原始列表,并在所有子列表上递归调用。因此,从大小 n 列表开始时调用g的次数是 g (n)= n·g (n-1)= n!

现在,让我们考虑一下该功能必须做的其他事情。递归的每一步的工作量包括:

  • 对于原始列表中的每个元素,构建一个少一个元素的新列表。这是等于 n 2
  • 的总工作量
  • 一旦知道了递归调用的结果,就将它添加到累加器中。这是等于 n 的总工作量(此部分可以忽略,因为过滤器成本更高)。

因此,既然我们知道将调用每个递归步骤多少次(基于我们之前的分析),非g相关工作的总量是: t (n)= n 2 + n(n-1) 2 + n(n-1)(n-2) 2 + ... + n!

这个公式看起来很痛苦,但实际上 t (n)/ n!具有有限的非零限制,因为 n 增加(它是 k + 1 / k! 0&lt; k&lt; n 的总和),所以 t (n)=Θ(n!)