我有一个在列表上工作的递归函数,该函数包含一个调用自身的循环,最后是另一个函数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
的阶乘成正比,是否有人可以确认?
答案 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!
现在,让我们考虑一下该功能必须做的其他事情。递归的每一步的工作量包括:
因此,既然我们知道将调用每个递归步骤多少次(基于我们之前的分析),非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!)。