尾递归调用尾递归

时间:2015-12-15 19:12:07

标签: recursion clojure functional-programming tail-recursion

我正试图用尾递归来解决pascal的三角形。我理解做尾递归,函数调用语句应该是最后一条指令。像这里:

(defn pascal [line colum, acc]
  (if (or (= line 0) (= line colum) (= colum 0))
    (+ acc 1)
    (recur (dec line) colum
           (pascal (dec line) (dec colum), acc))))

我的问题是:由于我使用递归调用作为递归的参数,它仍然有效吗?

因为我无法取代它:

(recur (dec line) colum
       (pascal (dec line) (dec colum), acc))))

对此:

(recur (dec line) colum
       (recur (dec line) (dec colum), acc))))

最好的问候

2 个答案:

答案 0 :(得分:3)

只有一半的呼叫是通过尾递归进行的,所以另一半可以吹掉堆栈。与此相比:

(defn factorial (n) 
  (if (= n 1)
      1
      (* n (factorial (n - 1)))))

这里(factorial (n - 1))需要在继续(* n <result>)之前完成,这是在递归运行时等待的堆栈帧。

它比没有尾部调用更好,但如果所有这些都是it is possible!

那么它会好得多

答案 1 :(得分:1)

也许我的答案与递归无关,而这个问题与 @Sylwester 的回答完全不同,但是显示解决此问题的另一种方法仍然有用。

假设pascal三角形具有以下属性:

  1. pascal三角形的每一行中的第一个和最后一个项目是“1”
  2. 第二个倒数第二个是行数
  3. 任何其他元素都可以通过公式解决:

  4. 意味着,您可以通过时间复杂度为O(n ^ 3)的线性算法求解pascal三角形的任何元素。它可能不比带有O(n ^ 2)和递归的递归版本酷,但它不会吹栈,它使用组合,这在我看来更好,因为它更简单,更安全。 所以,我们走了:

    (defn naive-factorial[n]
      (reduce *' (range 1 (inc n))))
    
    (defn combinatoric-formula[line pos]
      (if (<= pos line)
        (/ 
         (naive-factorial line) 
         (*' (naive-factorial pos) 
             (naive-factorial (- line pos))))))
    

    正如您所看到的,使用了naive-factorial函数,该函数采用n乘法,这导致我们得到O(n ^ 3)。它与你的函数相同,但没有任何递归。

    对于pascal三角形,还有不止一种方法可以用不同的方式解决它们。如果你有足够的时间,其中一些是非常棘手的,看一看:rosettacode.org

    同样在你的int中使用+数学在+'的clojure中使用,请在(defn pascal [row col] (let [aux (fn aux [tr tc prev acc] (cond (> tr row) (throw (.Exception "invalid input")) (and (= col tc) (= row tr)) (+' (first prev) (second prev)); the next number is the answer (= tc tr) (recur (+' tr 1) 1 (cons 1 acc) '(1)) ; new row :else (recur tr ; new column (+' tc 1) (next prev) (cons (+' (first prev) (second prev)) acc))))] (if (or (zero? col) (= col row)) 1 (aux 2 1 '(1 1) '(1))))) 函数中使用任何可能导致大数字的情况(假设这意味着添加将导致将您的值转换为biginteger类型,这允许非常大的数字。)

    我还将 @Sylwester 引入的方案版本翻译成clo j

    (time (combinatoric-formula 1000 100))
    "Elapsed time: 2.73794 msecs" for linear version
    
    (time (pascal 1000 100))
    "Elapsed time: 135.426888 msecs" for tail recursion version
    

    它可能可以改进,但显示了这个想法。它计算了从第三行到前一个提供的输入的整个三角形,然后得到答案。非常棒的和纯粹的功能方法魔术。

    此版本的性能比线性版本差得多。 它得到了:

    For h in hourly filter h.dt == DATE_ISO8601(14501160000000) 
       For i in instArr
          filter i.inst == h.inst
          update h with {"inst":i.inst, "dt":i.dt, "count":i.count} in hourly
    

    但它仍然更清洁,更凉爽;)