高阶导数1

高阶导数

有时在一个函数中,我们需要接受一个函数作为参数,返回一个函数作为一个返回值,如果一个函数里面对函数进行操作,我们称之为高阶导数。

函数作参数

例如下面的代码:

  1. 由1到n的连续和
  2. 由1到n立方的连续和
  3. 8 1 ∗ 3 + 8 3 ∗ 5 + 8 5 ∗ 7 + … \frac{8}{1*3 }+\frac{8}{3*5}+\frac{8}{5*7}+\dots 138+358+578+
def sum_naturals(n):
      total, k = 0, 1
      while k <= n:
          total, k = total + k, k + 1
      return total
def sum_cubes(n):
      total, k = 0, 1
      while k <= n:
          total, k = total + k*k*k, k + 1
      return total    
def pi_sum(n):
      total, k = 0, 1
      while k <= n:
          total, k = total + 8 / ((4*k-3) *(4*k-1)), k + 1
      return total

我们观察后可以发现,这三个代码实现极其的相似,具体可以概况为以下的形式

def <name>(n):
     total, k = 0 , 1
     while k <= n:
         total, k = total +  <term>(k), k + 1
     return total 

那么如何抽象函数进行实现,例如第一个例子:

def summation(n, term): 
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return total
    
def identity(x):
    return x
    
def sum_naturals(n):
    return summation(n, identity)

其他例子只需自己定义term函数即可

函数的广泛应用

例如我们要求黄金分割比例,接近1.6的一个数,我们可以采取update的一个函数不断进行更新逼近黄金比例,使用close函数比较是否满足既定目标。那么我们的框架就出来了。

def improve(update, close, guess=1):
      while not close(guess):
             guess = update(guess)
      return guess

接下来才是其他函数的设计

def golden_update(guess):
      return 1/guess + 1
      
def square_close_to_successor(guess):
      return approx_eq(guess * guess, guess + 1)
      
def approx_eq(x, y, tolerance=1e-15):
      return abs(x - y) < tolerance

我们直接调用即可

improve(golden_update, square_close_to_successor)

总结以下上面的例子:

  1. 对函数取个好名和函数的抽象化可以降低问题的复杂性
  2. 将大问题拆解为小问题从而易于实现

函数的嵌套

上面我们讨论了将函数作为参数进行传递,但是这种方法会使你的全局空间堆满小函数,还有一个问题就是我们受限于参数的传入,比如上面的update函数,我们一定要传入一个参数,不然会报错。那么嵌套函数来了。
比如说我们要计算一个数字的平方根。我们当然可以使用库函数sqrt()计算,现在提供另一种计算方法
反复计算sqrt_update可以逼近平方根。

def average(x, y):
      return (x + y)/2

def sqrt_update(x, a):
      return average(x, a/x)

如果按照以前的方法,我们还要定义一个close()函数,然后把两个函数传入improve函数里面去。现在提供一种新的方法。

def sqrt(a):
      def sqrt_update(x):
            return average(x, a/x)
      def sqrt_close(x):
            return approx_eq(x * x, a)
      return improve(sqrt_update, sqrt_close)

那么为什么要这样写,好处又是什么?
这涉及了作用范围以及环境的问题,在先前的方法中,我们将所以的函数都定义在全局环境里,作用范围为全局,而第二种方法定义在函数里面,作用范围为函数里面,不影响外层环境,层次更加清晰。

函数作为返回值

函数作为返回值时会保持它所在的环境,它也很好玩。
比如说当年我高中学的复合函数 h ( x ) = f ( g ( x ) ) h(x)=f(g(x)) h(x)=f(g(x))

def compose1(f, g):
    def h(x):
        return f(g(x))
    return h

给个例子

def square(x):
    return x * x

def successor(x):
    return x + 1

def compose1(f, g):
    def h(x):
         return f(g(x))
    return h

整活开始

牛顿法

听着很高大尚,实际上我们高中就学过关于导数的定义,用一条切线逼近曲线。而牛顿法可以通过不停的迭代得到函数的零点
我们需要一个函数 f 和它的导数 df ,更新方法为

def newton_update(f, df):
    def update(x):
        return x - f(x) / df(x)
    return update

那么如果我们想要找到函数的零点,我们就需要检查检查f(x)是否逼近0

def find_zero(f, df):
    def near_zero(x):
        return approx_eq(f(x), 0)
    return improve(newton_update(f, df), near_zero)

那么比如说我要计算 f ( x ) = x 2 − a f(x)=x^2-a f(x)=x2a的零点,我们可以找到它的导数 d f ( x ) = 2 x df(x)=2x df(x)=2x,我们可以得到代码如下

def square_root_newton(a):
    def f(x):
    	return x * x - a
   	def df(x):
   		return 2 * x
	return find_zero(f, df)

到了这里大家发现没有,这种函数的抽象,这种设计方法非常巧妙,我是学不会了,希望大家也可以写出这样优质的代码。
注:本文为作者学习compsing programs 的笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值