python 裘宗燕 第三章2 ppt

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
裘宗燕,2017//-2-

函数定义和计算试验(用38、39、40等实验)
从问题到程序(Python程序设计)
计算的时间

函数 fib 的定义很好,与数学定义直接对应,简单、易理解 但也不好:计算要花费很长时间(计算fib(100)需要百万年) 现在的计算机非常快,但计算也要花时间


有些程序的计算可能花很长时间 解决某些复杂问题的程序必然需要非常长的时间

有些程序可能需要运行很长时间 例:调和级数部分和,计算这个级数的前多少项之和能超过某 个给定的数

有关终止性的理论结果:程序终止性问题是不可判定的

不存在判断任何程序对任何输入是否终止的有效方法 直观说,就是无法写出一个软件,判断任意的一个程序对任 意一个输入是否终止
裘宗燕,2017//-17-

递归函数(递归程序)存在类似的问题 其终止性同样是不可判定的
从问题到程序(Python程序设计)
新函数效率高,但论证其正确性则不容易(有得有失)
裘宗燕,2017//-7-
求最大公约数

考虑求最大公约数的问题。定义:两个数 m 和 n 的最大公约数, 就是既能整除 m 又能整除 n 的最大的正整数


最简单而直接的想法是用一系列的正整数试验
一种考虑

从 1 开始试,1 总可以整除 m 和 n 递增,发现同时为 m 和 n 约数时记录它 试到超过 m 和 n 中较小的一个,就可以结束
从问题到程序(Python程序设计)
裘宗燕,2017//-9-
求最大公约数:辗转相除

函数定义(递归实现): def gcd(m, n): if n % m == 0: return m return gcd(n % m, m)

函数定义(循环实现):
def gcd(m, n): if m == 0: return n while n % m != 0: m, n = n % m, m return m
从问题到程序(Python程序设计)
循环程序和终止性

例(Collatz conjecture,猜想下面函数对每个 n 终止): def collatz(n): while n != 1: if n%2 == 0 : # n is even n = n//2 else: # n is odd n=n*3+1 /wiki/Collatz_conjecture 可检查它迭代多少次,或达到的最大数值等

求最大公约数的经典算法是辗转相除法,又称为欧几里得算法。 它利用下面关系
n m modn 0 gcd( m, n) gcd( n, m modn) m modn 0

利用这个关系实现一个求 gcd 的函数 改造得到的函数,增加检查,可以能得到一个可以对任一 对整数求最大公约数的函数


用了一个硬币 a 之后 n – a 的兑换方式数,加上 不用硬币 a 时 n 的兑换方式数

上面是递归。有几种(基础)情况可以直接得到结果:

n 等于 0,说明找到一种兑换方式(1 种兑换方式) n 小于 0,说明没找到兑换方式(0 种兑换方式)

货币数等于 0 说明没找到兑换方式(0 种兑换方式)
计算有代价

实际计算由现实世界中的设备(目前是电子计算机)完成

不是数学,不是抽象的理论 每步计算都有代价,即使非常小但也不为 0,累积起来就可 能超过给定的任意大的数(这是数学)

有代价是计算的一种本质特征,相关理论问题后面讨论 一些情况:

可能存在从理论上看能用计算机解决的问题,但我们不可能 等到计算机给出解的那个时间 编程时需要考虑计算的时间,寻找解决问题的高效方法(算 法)永远有意义 有些实际问题对计算完成的时间非常敏感 例:ABS/EBD(电子制动分配)系统,天气预报系统等


初步想想好像没有明显的规律
解决问题关键是看到问题的递归性质 假设能把63个圆盘从A搬到C(用B辅助),把A的最后圆盘直接 搬到B,再把C上63个圆盘搬到B,搬63个与搬64个类似。分步: 这样就把搬64个圆盘的问题归结为两次搬63个圆盘的问题 不难定义解决问题的递归函数,需要引进表示圆盘个数的参数
ccoins 定义一个主函数,调用 ccoins(6, n)

这个函数用递归的方式写,很简单

函数定义直接对应于前面的分析 也可以用循环的方式写出程序,但比较复杂,也不那么清晰。 有编程经验的同学自己试试,需要用一些高级技术
裘宗燕,2017//-14-
从问题到程序(Python程序设计)
兑换硬币


不容易把这个问题转为循环程序(可以做,但比较复杂)
裘宗燕,2017//-12-
从问题到程序(Python程序设计)
递归实例:换硬币

一个递归程序实例 也可以用循环的方式写程序,但比较复杂,也不那么清晰

人民币硬币有 1 分、2 分、5 分、10 分、50 分和 100 分。给定 一定款额,问将其换成硬币有多少种不同兑换方式 递归的看法:币值 n 的兑换方式数等于
基本编程技术-2

递归与循环
计算Fibonacci的函数 函数运行时间 求最大公约数 两个递归函数实例
河内塔问题,换硬币问题

简单递归和相互递归

程序终止性
裘宗燕,2017//-1-
从问题到程序(Python程序设计)
Fibonacci 序列(递归程序示例)

Fibonacci(斐波那契)序列是一个重要的整数序列 因意大利数学家而斐波那契(1175-1250)命名,最早源于印度 有大量数学结果,在计算领域有许多特别重要的应用

为什么需要递归定义?

不少算法用递归的方式描述简单而自然,正确性很容易看清 楚。用循环方式描述却很难/复杂/意义不清晰
后面学习中还会看到,在计算中需要处理的许多结构本身就 是递归的,用递归的方式处理非常自然
裘宗燕,2017//-11-从问题到程序(Pyth Nhomakorabean程序设计)
河内塔(梵塔)问题

问题描述:神庙有三根细长玉石圆柱, 64个大小不等的有孔金盘,下大上小 套在圆柱A上(梵塔)。需要把圆盘从 该圆柱搬到圆柱B,规则是每次搬一个 圆盘,不允许把大盘放到小盘上。要 求写程序模拟这个过程
裘宗燕,2017//-4-
从问题到程序(Python程序设计)
为计算计时

统计程序或程序片段的计算时间,有助于理解程序的性质,实际 中常需要做。现在介绍Python标准库的计时功能

标准库包time提供了计时函数time,调用它得到从1970年1月1 日开始的秒数。两次调用time就能为一段计算计时。例如:
from time import time def test_fib(n): t = time() f = fib(n) print("Fib[" + str(n) + "] =", f, "Timing:", str(time() - t) + "s\n") 函数的输出计算 fib(n) 的时间


考虑定义一个基于递推循环的函数,从 F0 和 F1 出发,一步步向 前递推,直至得到所需要的 Fn 考虑循环中的变量安排:


用 f1 和 f2 记录已知的两个相邻 Fibonacci 值 f1 + f2 是下一个 Fib 值
需要知道算到第几个 Fib 值,k 记录变量 f1 的值是 Fk
裘宗燕,2017//-15-
相互递归

有时需要两个或多个函数相互调用

例如在 f 里调用 g,在 g 里调用 f 这样的函数称为相互递归的函数

Python(只)要求函数在使用时必须有定义

前面定义的函数可以调用后面定义的函数
只要实际调用时被调用函数已经定义

定义函数时,用到的函数可以没定义,但在执行程序中调用 函数时,被调用的函数必须有定义

从问题到程序(Python程序设计)
还要加上特殊情况处理。还需考虑如何论证循环程序的正确性 (同样需要借助循环不变式的概念)
裘宗燕,2017//-10-
递归和循环

是不是递归函数一定比循环函数慢? 为什么考虑递归程序有价值?

回答:

不一定
例如 Fibonacci 的递归和循环实现采用了不同算法,完全可 以用递归实现第二种算法

举例:两个相互递归的函数
从问题到程序(Python程序设计)
裘宗燕,2017//-16-
终止性和循环程序

写程序时,需要考虑程序是否必定终止(对所有可能的输入都终 止)。程序不保证终止,就不保证能给出最后结果

直线型和分支程序必然终止(只要每个基本语句终止)
循环程序则可能不终止(即使每个基本语句都终止)

Fibonacci 序列的定义
• •
F0 = 0, F1 = 1
(基础情况)
Fn = Fn-1 + Fn-2, 对所有 n > 1(一般情况)
序列的前几项:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ......

Fibonacci 序列的定义是递归定义

基础情况,一般情况可归结到更简单情况(下标更小) 可以直接做出一个递归定义的函数
如果发现一个程序很慢,不能满足实际需要,可能做的一件事就 是仔细检查程序,估计其中各部分的时间开销,设法改进
裘宗燕,2017//-5-
从问题到程序(Python程序设计)
再看 Fibonacci 序列:另一种算法

对 Fibonacci 数的计算,换一种看法: 基本情况已直接给定,由 Fn-1 和 Fn 可以递推得到 Fn+1
def ccoins(k, n): if n == 0: return 1 if k == 0 or n < 0: return 0 return ccoins(k, n - amount(k)) + ccoins(k - 1, n)
从问题到程序(Python程序设计)
def num_coins(n): return ccoins(6, n)
fib(5) fib(4) fib(3) Fib(2) fib(1) fib(0)
裘宗燕,2017//-3-

看计算 fib(5) 的情况 存在大量重复计算
fib(3) fib(2) fib(2) fib(1) fib(0) fib(1)
fib(1)
fib(1)
fib(0)
从问题到程序(Python程序设计)
记录的最大的那个约数,就是 m 和 n 的最大公约数

假定 m >= 0, n > 0,容易写出函数定义 如果允许参数是任何整数(包括负数),需要加一些前处理
从问题到程序(Python程序设计)
裘宗燕,2017//-8-
求最大公约数:辗转相除

还可以从大到小循环检查 找到的第一个能同时整除两个数的 d 就是最大公约数
裘宗燕,2017//-13-
从问题到程序(Python程序设计)
递归实例:换硬币

考虑算法的实现,其中有些问题需要解决 为取得硬币的币值,把硬币排顺序后编号 1 到 6 对应到 1 分到 100 分的硬币 定义一个函数,实现这种对应。减一种硬币时编号范围减一

前面有关分析可以直接翻译为一个递归定义的函数
f1, f2 和 k 需要在每次循环中更新 f1, f2 = f2, f1+f2 k += 1 也可以用递归函数描述这种计算 存在更快的算法
裘宗燕,2017//-6-
从问题到程序(Python程序设计)
循环不变式

怎么确定下面循环能算出正确的结果? f1, f2 = 0, 1 # 开始时分别表示F_0和F_1 k=0 while k < n: f1, f2 = f2, f2 + f1 k += 1 return f1

硬币编号函数: def amount(k): if k == 1: return 1 if k == 2: return 2 if k == 3: return 5 if k == 4: return 10 if k == 5: return 50 if k == 6: return 100

主要函数:

需要论证一旦k增加到n,f1的值就是第n个Fibonacci数 我们说明循环保证:每次条件判断时,f1总是第k个Fibonacci 数,f2总是第k+1个数。这样当k值等于n时f1就是所需结果 在循环中总能保证的变量之间的关系,称为循环不变式 循环不变式是证明循环功能正确的基本工具

从问题到程序(Python程序设计)
相关文档
最新文档