【问题标题】:SICP 1.45 - Why are these two higher order functions not equivalent?SICP 1.45 - 为什么这两个高阶函数不等价?
【发布时间】:2019-05-24 08:32:02
【问题描述】:

我正在完成 [SICP][1] 中的练习,我想知道是否有人可以解释这两个看似等效但给出不同结果的函数之间的区别!是因为四舍五入吗??我在想函数的顺序在这里应该无关紧要,但不知何故呢?有人可以解释这里发生了什么以及为什么不同吗?

详情:

练习 1.45:..看到找到 y => x/y 的不动点并不 收敛,这可以通过平均阻尼来解决。相同 方法适用于找到立方根作为固定点 平均阻尼y => x/y^2。不幸的是,该过程不起作用 对于第四根——单个平均阻尼不足以产生 定点搜索y => x/y^3 收敛。

另一方面,如果我们 平均阻尼两次(即,使用平均阻尼的平均阻尼 y => x/y^3) 定点搜索确实收敛。做一些实验 确定计算第 n 个根需要多少平均阻尼 作为基于y => x/y^(n-1)的重复平均阻尼的定点搜索。

使用它来实现计算根的简单过程 使用fixed-pointaverage-damprepeated 过程 练习 1.43。假设您需要的任何算术运算都是 可作为原语使用。

我的回答(注意repeataverage-damping的顺序):

(define (nth-root-me x n num-repetitions)
  (fixed-point (repeat (average-damping (lambda (y)
                                           (/ x (expt y (- n 1)))))
                       num-repetitions)
               1.0))

我看到了另一种 Web 解决方案,其中直接在 average damp 上调用 repeat,然后使用参数调用该函数

(define (nth-root-web-solution x n num-repetitions)
      (fixed-point
         ((repeat average-damping num-repetition)
          (lambda (y) (/ x (expt y (- n 1)))))
         1.0))

现在调用这两个,答案似乎有所不同,我不明白为什么!我的理解是函数的顺序不应该影响输出(它们是关联的吗?),但显然它是!

> (nth-root-me 10000 4 2)
> 
> 10.050110705350287
> 
> (nth-root-web-solution 10000 4 2)
> 
> 10.0

我做了更多的测试,总是这样,我的答案很接近,但另一个答案几乎总是更接近!有人可以解释发生了什么吗?为什么这些不等价?我的猜测是调用这些函数的顺序搞砸了,但它们似乎与我有关联。

例如:

(repeat (average-damping (lambda (y) (/ x (expt y (- n 1)))))
         num-repetitions)

((repeat average-damping num-repetition)
 (lambda (y) (/ x (expt y (- n 1)))))

其他辅助函数:

(define (fixed-point f first-guess)
  (define (close-enough? v1 v2)
    (< (abs (- v1 v2))
       tolerance))
  (let ((next-guess (f first-guess)))
    (if (close-enough? next-guess first-guess)
        next-guess
        (fixed-point f next-guess))))

(define (average-damping f) 
  (lambda (x) (average x (f x))))

(define (repeat f k)
  (define (repeat-helper f k acc)
    (if (<= k 1)
        acc
           ;; compose the original function with the modified one
        (repeat-helper f (- k 1) (compose f acc)))) 
  (repeat-helper f k f))

(define (compose f g)
  (lambda (x)
    (f (g x))))

【问题讨论】:

    标签: scheme lisp higher-order-functions sicp


    【解决方案1】:

    您在问为什么“两个看似等效的函数”会产生不同的结果,但实际上这两个函数却大不相同。

    让我们尝试简化问题,看看它们为何不同。这两个函数之间的唯一区别是两个表达式:

    (repeat (average-damping (lambda (y) (/ x (expt y (- n 1)))))
            num-repetitions)
    
    ((repeat average-damping num-repetition)
       (lambda (y) (/ x (expt y (- n 1)))))
    

    为了简化我们的讨论,我们假设num-repetition 等于 2,以及一个比 lambda 更简单的函数,例如以下函数:

    (define (succ x) (+ x 1))
    

    所以两个不同的部分现在是:

    (repeat (average-damping succ) 2)
    

    ((repeat average-damping 2) succ)
    

    现在,对于第一个表达式,(average-damping succ) 返回一个 numeric 函数,该函数计算参数与其后继参数之间的平均值:

    (define h (average-damping succ))
    (h 3) ; => (3 + succ(3))/2 = (3 + 4)/2 = 3.5
    

    所以,表达式(repeat (average-damping succ) 2) 等价于:

    (lambda (x) ((compose h h) x)
    

    相当于:

    (lambda (x) (h (h x))
    

    同样,这是一个 numeric 函数,如果我们将此函数应用于 3,我们有:

    ((lambda (x) (h (h x)) 3) ; => (h 3.5) => (3.5 + 4.5)/2 = 4
    

    在第二种情况下,我们有 (repeat average-damping 2) 产生完全不同的功能:

    (lambda (x) ((compose average-damping average-damping) x)
    

    相当于:

    (lambda (x) (average-damping (average-damping x)))
    

    您可以看到这次的结果是一个高级函数,而不是一个整数函数,它接受一个函数x 并对其应用两次average-damping 函数。让我们通过将此函数应用于succ,然后将结果应用于数字 3 来验证这一点:

    (define g ((lambda (x) (average-damping (average-damping x))) succ))
    (g 3) ; => 3.25
    

    结果的差异不是由于数值近似,而是由于不同的计算:首先(average-damping succ)返回函数h,该函数计算参数与其后继值之间的平均值;然后(average-damping h) 返回一个新函数,该函数计算参数和函数h 的结果之间的平均值。这样的函数,如果传入像 3 这样的数字,首先计算 3 和 4 之间的平均值,即 3.5,然后计算 3(再次作为参数)和 3.5(上一个结果)之间的平均值,得到 3.25。

    【讨论】:

    • 这是一个了不起的答案,我的关键点/啊哈时刻是当您命名并简化 lambda 函数然后定义 h。当你以这种方式展示它时,目标是什么变得非常明显,甚至这个问题对于为什么该方法有效以及为什么它会收敛更有意义!再次感谢!
    【解决方案2】:

    repeat的定义包含

      ((repeat f k) x)  =  (f (f (f (... (f x) ...)))) 
                        ;   1  2  3       k
    

    总共有k 嵌套调用f。让我们把它写成

                        =  ((f^k) x)
    

    也定义

      (define (foo n)  (lambda (y)  (/ x (expt y (- n 1)))))
      ;       ((foo n)  y)  =  (/ x (expt y (- n 1)))
    

    然后我们有

      (nth-root-you x n k)  =  (fixed-point  ((average-damping (foo n))^k)   1.0)
    
      (nth-root-web x n k)  =  (fixed-point  ((average-damping^k)  (foo n))  1.0)
    

    因此,您的版本在 fixed-point 执行的每个迭代步骤中使用 once-average-damped (foo n) 函数执行 k 步骤时间>;网络使用 k-times-average-damped (foo n) 作为 its 迭代步骤。请注意,无论使用多少次,once-average-damped 函数仍然只有 once 平均阻尼,并且使用它几次 时间可能只会加剧问题,而不是解决问题。

    对于k == 1,生成的两个迭代步长函数当然是等价的。

    在你的情况下k == 2,等等

    (your-step y)  =  ((average-damping (foo n)) 
                         ((average-damping (foo n))  y))                 ; and,
    
    (web-step  y)  =  ((average-damping (average-damping (foo n)))  y)
    

    自从

    ((average-damping f)  y)  =  (average  y  (f y))
    

    我们有

    (your-step y)  =  ((average-damping (foo n)) 
                         (average  y  ((foo n)  y)))
                   =  (let ((z (average  y  ((foo n)  y))))
                         (average  z  ((foo n)  z)))
    
    (web-step  y)  =  (average y  ((average-damping (foo n))  y))
                   =  (average y   (average y   ((foo n)  y)))
                   =  (+ (* 0.5 y)  (* 0.5 (average y  ((foo n)  y))))
                   =  (+ (* 0.75 y)  (* 0.25 ((foo n)  y)))
        ;; and in general:
        ;;         =    (2^k-1)/2^k * y  +  1/2^k * ((foo n)  y)
    

    区别很明显。平均阻尼用于抑制(foo n)在一定ys处可能出现的不稳定跳跃,k越高,阻尼效果越强,从上一个公式可以清楚地看出。

    【讨论】:

      猜你喜欢
      • 2016-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多