Performance Scheme—加速大量递归函数

Performance Scheme—加速大量递归函数,performance,recursion,optimization,scheme,racket,Performance,Recursion,Optimization,Scheme,Racket,我必须编写一个Scheme谓词来计算函数f(N->N),定义为: 如果n

我必须编写一个Scheme谓词来计算函数f(N->N),定义为:

  • 如果n<4:f(n)=(n^2)+5
  • 如果n≥ 4:f(n)=[f(n−1) +f(n)−2) ]*f(n)−(四)
我写了一个简单的谓词:

(define functionfNaive
  (lambda (n)
    (if (< n 4) (+ (* n n) 5)
        (* (+ (functionfNaive (- n 1)) (functionfNaive (- n 2))) 
           (functionfNaive (- n 4))))))
(定义函数fnaive
(λ(n)
(如果(
现在,我尝试了一种使用累加器的方法,但它不起作用。。。 我的代码:

(define functionf
  (lambda(n)
    (functionfAux n 5 9 14)))

(define functionfAux
 (lambda (n n1 n2 n4)
   (cond
     [(< n 4) (+ (* n n) 5)]
     [(= n 4) (* n1 (+ n2 n4))]
      [else (functionfAux (- n 1) n2 n4 (* n1 (+ n2 n4)))])))
(定义函数f
(λ(n)
(功能编号5 9 14)
(定义函数faux)
(λ(n n1 n2 n4)
(续)
[(
根据要求,这里有一个代码的记忆版本,它的性能比原始版本要好:

(define functionf
  (let ((cache (make-hash)))
    (lambda (n)
      (hash-ref!
       cache
       n
       (thunk
        (if (< n 4)
            (+ (* n n) 5)
            (* (+ (functionf (- n 1)) (functionf (- n 2))) (functionf (- n 4)))))))))
如果您需要,这里有一个通用的记忆程序:

(define (memoize fn)
  (let ((cache (make-hash)))
    (λ arg (hash-ref! cache arg (thunk (apply fn arg))))))
在你的情况下,可以像

(define functionf
  (memoize
   (lambda (n)
     (if (< n 4) 
         (+ (* n n) 5)
         (* (+ (functionf (- n 1)) (functionf (- n 2))) (functionf (- n 4)))))))
(定义函数f
(回忆)
(λ(n)
(如果(
根据要求,这里有一个代码的记忆版本,它的性能比原始版本要好:

(define functionf
  (let ((cache (make-hash)))
    (lambda (n)
      (hash-ref!
       cache
       n
       (thunk
        (if (< n 4)
            (+ (* n n) 5)
            (* (+ (functionf (- n 1)) (functionf (- n 2))) (functionf (- n 4)))))))))
如果您需要,这里有一个通用的记忆程序:

(define (memoize fn)
  (let ((cache (make-hash)))
    (λ arg (hash-ref! cache arg (thunk (apply fn arg))))))
在你的情况下,可以像

(define functionf
  (memoize
   (lambda (n)
     (if (< n 4) 
         (+ (* n n) 5)
         (* (+ (functionf (- n 1)) (functionf (- n 2))) (functionf (- n 4)))))))
(定义函数f
(回忆)
(λ(n)
(如果(
首先,这不是谓词。谓词是返回布尔值的函数

要计算第n个结果,请从前四个元素开始计算,并保持最后四个已知元素。达到n时停止:

(define (step a b c d n)
  (list b c d (* (+ c d) a)) (+ n 1)))

等等,很简单。第一个调用将是
(步骤5 6 9 14 3)

首先,这不是谓词。谓词是返回布尔值的函数

要计算第n个结果,请从前四个元素开始计算,并保持最后四个已知元素。达到n时停止:

(define (step a b c d n)
  (list b c d (* (+ c d) a)) (+ n 1)))

等等,很简单。第一个调用将是
(步骤5 6 9 14 3)

递归树的深度可能是最大的问题,因此可以使用迭代,这意味着使用一些变量来存储中间过程

#lang racket
(define (functionf n)
        (define (iter now n1 n2 n3 n4 back)
          (if (= n now)
              back
              (iter (+ now 1) back n1 n2 n3 (* n3 (+ back n1)))))
        (if (< n 4)
            (+ 5 (* n n))
            (iter 4 14 9 6 5 125)))

(functionf 5)
#朗球拍
(定义(函数f n)
(定义(iter现在n1 n2 n3 n4返回)
(如果(=n现在)
返回
(国际热核实验堆(+现在为1)返回n1 n2 n3(*n3(+返回n1()()))
(如果(

这样,堆栈的深度仅为1,代码的速度加快。

递归树的深度可能是最大的问题,因此可以使用迭代,这意味着使用一些变量来存储中间进程

#lang racket
(define (functionf n)
        (define (iter now n1 n2 n3 n4 back)
          (if (= n now)
              back
              (iter (+ now 1) back n1 n2 n3 (* n3 (+ back n1)))))
        (if (< n 4)
            (+ 5 (* n n))
            (iter 4 14 9 6 5 125)))

(functionf 5)
#朗球拍
(定义(函数f n)
(定义(iter现在n1 n2 n3 n4返回)
(如果(=n现在)
返回
(国际热核实验堆(+现在为1)返回n1 n2 n3(*n3(+返回n1()()))
(如果(


这样,堆栈深度仅为1,代码速度加快。

在这里使用累加器有什么意义?运行时间优化?您是否愿意进行记忆化(与您的过程配合得非常好)?@uselpa您可以演示如何进行记忆化吗?我还没有在racket中使用回忆录。@naomik实现回忆录的一个简单方法是创建一个哈希表,参数作为键,结果作为值。有趣的部分是编写一个函数适配器来在幕后完成所有这些工作,这样您就可以像
(define func(memoise(lambda…))一样简单地记忆函数了。
:-D@uselpa这是为了更好的表现。在这里,如果n太大,我就无法计算f。@ChrisJester-Young编写
备忘录
适配器听起来很有趣。也许是你下一篇博客文章的想法?在这里使用累加器有什么意义?运行时间优化?您是否愿意进行记忆化(与您的过程配合得非常好)?@uselpa您可以演示如何进行记忆化吗?我还没有在racket中使用回忆录。@naomik实现回忆录的一个简单方法是创建一个哈希表,参数作为键,结果作为值。有趣的部分是编写一个函数适配器来在幕后完成所有这些工作,这样您就可以像
(define func(memoise(lambda…))一样简单地记忆函数了。
:-D@uselpa这是为了更好的表现。在这里,如果n太大,我就无法计算f。@ChrisJester-Young编写
备忘录
适配器听起来很有趣。也许你的下一篇博文有什么想法?我正在修补一个通用的记忆程序和神圣的烟雾,你的比我想象的要简单和优雅得多。非常酷的回答。有没有可能采用一个写得不好的过程,比如
(define(badfib x)(if(
,然后将其记忆起来<代码>(定义goodfib(memoize badfib))
。我的直觉是这是不可能的,因为
badfib
内部引用自身,而不是记忆版本,
goodfib
。即使它不实用,我想如果你能用一个通用的
memoize
适配器来记忆一个已有的函数,那就太酷了。@naomik是的。我假设
badfib
在全局范围内,因此可以执行
(let((oldfib-badfib))(set!badfib(memoize-oldfib)))
。第一次是O(n),第二次是O(1)秒。@Sylwester有没有办法让原始的
badfib
保持原样?@naomik如果你在代码中定义过程,你可以制作一个宏,它可以生成两个版本,其中递归调用被封装在一个词法变量中,以局部更改递归的位置。我在修补一个通用的记忆程序