fib函数用python编写_Python中利用函数装饰器实现备忘功能

“备忘”的定义

“memoization”(备忘)这个词是由Donald Michie在1968年提出的,它基于拉丁语单词“memorandum”(备忘录),意思是“被记住”。虽然它和单词“memorization”在某种程度上有些相似,但它并不是该单词的错误拼写。实际上,Memoisation是一种用于通过计算来加速程序的技术,它通过记住输入量的计算结果,例如函数调用结果,来实现其加速目的。如果遇到相同的输入或者具有相同参数的函数调用,那么之前存储的结果就可以被再次使用,从而避免一些不必要的计算。在很多情况下,可以使用一个简单的数组来存储结果,但也可以使用许多其他的数据结构,例如关联数组,它在Perl语言中叫做哈希,在Python语言中称为字典。

备忘功能可以由程序员显式地编程实现,但是一些编程语言如Python,都提供了自动备忘函数的机制。

利用函数装饰器实现备忘功能

在前面关于递归函数的那章中,我们分别使用迭代和递归实现了斐波纳契数列的求解。我们已经证明,如果直接利用斐波纳契数列的数学定义,在一个递归函数中实现数列的求解,正如下面的函数一样,那么它将具有指数级的时间复杂度:

def fib(n):

if n == 0:

return 0

elif n == 1:

return 1

else:

return fib(n-1) + fib(n-2)

此外,我们还提出了一种提高递归实现的时间复杂度的方法,即通过添加一个字典来记住之前函数的计算结果。这是一个显式地使用备忘技术的例子,只是当时我们并没有这么称呼它。这种方法的缺点是,原始递归实现的明晰性和优雅性丢失了。

造成以上缺点的原因是,我们改变了递归函数fib的代码。不过下面的代码不会改变我们的fib函数,所以它的明晰性和易读性并没有丢失。为了实现该目的,我们使用自定义的函数memoize()。函数memoize()以函数作为参数,并使用一个字典“memo”来存储函数的结果。虽然变量“memo”和函数“f”仅仅具有局部备忘功能,但是它们通过函数“helper”被一个闭包捕获,而memoize()将函数“helper”作为引用返回。所以,对memoize(fib)的调用将会返回一个helper()的引用,而在helper()中实现了fib()函数的功能以及一个用于保存还未存储的结果到字典“memo”中的包装器,并防止重新计算“memo”中已有的结果。

def memoize(f):

memo = {}

def helper(x):

if x not in memo:

memo[x] = f(x)

return memo[x]

return helper

def fib(n):

if n == 0:

return 0

elif n == 1:

return 1

else:

return fib(n-1) + fib(n-2)

fib = memoize(fib)

print(fib(40))

现在让我们了解下所谓的装饰器,首先看一下上面代码中将备忘功能指派到fib函数的这一行:

fib = memoize(fib)

一种说法是,函数memoize()装饰了函数fib。

将Memoize封装成类

我们还可以将结果的缓存封装到一个类中,如下面的例子所示:

class Memoize:

def __init__(self, fn):

self.fn = fn

self.memo = {}

def __call__(self, *args):

if args not in self.memo:

self.memo[args] = self.fn(*args)

return self.memo[args]

因为我们使用了字典,所以不能使用可变参数,即参数必须是不可变的。

Python中的装饰器

Python中的装饰器是一个可调用的Python对象,用于修改一个函数、方法或者类的定义。原始的对象,也就是即将被改变的那个对象,作为参数传递给一个装饰器,而装饰器则返回一个修改过的对象,例如一个修改过的函数,它会被绑定到定义中使用的名字上。Python中的装饰器与Java中的注解有一个相似的语法,即Python中的装饰器语法可以看作是纯粹的语法糖,使用“@”作为关键字。

示例:使用装饰器实现备忘功能

其实,前面我们已经使用了装饰器,只是没有这么称呼它而已。实际上,本章开头例子中的memoize函数就是一个装饰器,我们使用它来记住fib函数的结果,只是我们没有使用Python中装饰器特殊的语法而已,即艾特字符“@”。

相比于写成下面的形式

fib = memoize(fib)

我们可以这样写

@memoize

但这一行必须直接写在被装饰的函数之前,在我们的例子fib()中,如下所示:

def memoize(f):

memo = {}

def helper(x):

if x not in memo:

memo[x] = f(x)

return memo[x]

return helper

@memoize

def fib(n):

if n == 0:

return 0

elif n == 1:

return 1

else:

return fib(n-1) + fib(n-2)

#fib = memoize(fib)

print(fib(40))

利用装饰器检查参数

在讲解递归函数的那章中我们介绍了阶乘函数,在那里我们希望保持函数尽可能简单,而不想掩盖基本理念,所以代码中没有包含任何参数检查代码。然而,如果别人以负数或者浮点数作为参数来调用我们的函数,那么函数将会陷入一个死循环。

下面的程序使用一个装饰器函数来确保传给函数“factorial”的参数是一个正整数:

def argument_test_natural_number(f):

def helper(x):

if type(x) == int and x > 0:

return f(x)

else:

raise Exception("Argument is not an integer")

return helper

@argument_test_natural_number

def factorial(n):

if n == 1:

return 1

else:

return n * factorial(n-1)

for i in range(1,10):

print(i, factorial(i))

print(factorial(-1))

练习

1、我们的练习是一个古老的谜题。1612年,法国耶稣会士Claude-Gaspar Bachet提出了该谜题,即使用一个天平称出从1磅到40磅的所有整数重量的东西(例如,糖或者面粉),求最少的砝码数量。

第一个方法可能是使用1、2、4、8、16和32磅重量的这些砝码。如果我们将砝码放在天平的一端,而将物品放在另一端,那么这种方法用到的砝码数量将是最小的。然而,我们也可以将砝码同时放在天平的两端,此时我们仅仅需要重量为1、3、9、27的砝码。

编写一个Python函数weigh(),该函数计算需要的砝码以及它们在天平盘中的分布,以此来称量1磅到40磅中任何一个整数重量的物品。

解决方法

1、我们需要前面章节“Linear Combinations”中的函数linear_combination()。

def factors_set():

factors_set = ( (i,j,k,l) for i in [-1,0,1]

for j in [-1,0,1]

for k in [-1,0,1]

for l in [-1,0,1])

for factor in factors_set:

yield factor

def memoize(f):

results = {}

def helper(n):

if n not in results:

results[n] = f(n)

return results[n]

return helper

@memoize

def linear_combination(n):

""" returns the tuple (i,j,k,l) satisfying

n = i*1 + j*3 + k*9 + l*27 """

weighs = (1,3,9,27)

for factors in factors_set():

sum = 0

for i in range(len(factors)):

sum += factors[i] * weighs[i]

if sum == n:

return factors

2、利用上面的代码,就能很容易写出我们的函数weigh()。

def weigh(pounds):

weights = (1,3,9,27)

scalars = linear_combination(pounds)

left = ""

right = ""

for i in range(len(scalars)):

if scalars[i] == -1:

left += str(weights[i]) + " "

elif scalars[i] == 1:

right += str(weights[i]) + " "

return (left,right)

for i in [2,3,4,7,8,9,20,40]:

pans = weigh(i)

print("Left pan: " + str(i) + " plus " + pans[0])

print("Right pan: " + pans[1] + "n")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/453644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作

MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作 上一篇博文MyBatis学习总结(一)——MyBatis快速入门中我们讲了如何使用Mybatis查询users表中的数据,算是对MyBatis有一个初步的入门了,今天讲解一下如何使用MyBatis对users表执行CRUD操作。本文中使…

cifs mount 挂载共享目录_安装cifsutils解决linux挂载windows共享文件夹

1、安装mount.cifs软件包yum install cifs-utils -y如果是离线环境,请参考我的另一篇文章https://blog.csdn.net/qq_37119960/article/details/1083313732、开始挂载mount.cifs //192.168.1.110/share /usr/local/winshare -o useradministrator,pass123456参数说明…

JFinal框架

FJinal过滤器(tomcat) 创建java类继承JFinalConfig 会实现六个方法(有一个是拦截器的方法好像是,那个我好像看的跟struts2一样但是又没看懂暂时不写) Controller层的测试方法 Entity实体类 常用方法 查询 增加 删除 修改 转载于:https://www.cnblogs.com/guanzhuang/p/8317949.…

掌握 Linux 调试技术 使用 GDB 调试 Linux 软件

简介: 您可以用各种方法来监控运行着的用户空间程序:可以为其运行调试器并单步调试该程序,添加打印语句,或者添加工具来分析程序。本文描述了几种可以用来调试在 Linux 上运行的程序的方法。我们将回顾四种调试问题的情况&#xf…

集合之二:迭代器

迭代器的简单使用 在遍历容器时,我们可以使用for循环或者是增强for循环,但是不同的集合结构在遍历时,我们要针对集合特点采取不同的方式,比如List是链表,我们可以直接当做数组处理,但Map是Key—Value的形式…

简单使用ansible-playbook

1.使用以下命令给客户端安装httpd服务: [rootserver ~]# ansible testhost -m yum -a "namehttpd" 192.168.77.128 | SUCCESS > {"changed": true, "msg": "", "rc": 0, "results": ["Loaded …

原则

昨天例会上,领导分享了他最近看过的一本书《原则》。试想,工作上,生活中我的原则是什么呢?关于技术学习的原则。一开始的时候,一般都是遇到不会的再去学习,我一直比较喜欢带着问题,这样会学习效…

Python内置函数简记

一、数学运算类 abs(x)求绝对值 1、参数可以是整型,也可以是复数 2、若参数是复数,则返回复数的模complex([real[, imag]])创建一个复数divmod(a, b)分别取商和余数 注意:整型、浮点型都可以float([x])将一个字符串或数转换为浮点数。如果无参…

开源Java反编译工具

Java 反编译器 1. JD-GUI JD-GUI 是一个用 C 开发的 Java 反编译工具,由 Pavel Kouznetsov开发,支持Windows、Linux和苹果Mac Os三个平台。 而且提供了Eclipse平台下的插件JD-Eclipse。JD-GUI不需要安装,直接点击运行,可以反编译j…

基于MPI的H.264并行编码代码移植与优化

2010 03 25基于MPI的H.264并行编码代码移植与优化范 文洛阳理工学院计算机信息工程系 洛阳 471023摘 要 H.264获得出色压缩效果和质量的代价是压缩编码算法复杂度的增加。为了寻求更高的编码速度,集群并行计算被运用到H.264的视频编码计算中。分析H.264可实现并行计…

python自动取款机程序_python ATM取款机----运维开发初学(上篇)

自动取款机基本功能:可以存取转账,刷卡信息查询,银行卡号历史信息查询,消费记录查询,修改密码。思维导图如下:数据库设计:mysql> desc balan_list; #保存账号交易记录option_type-----------…

java的运行参数

贴个java的运行参数: Usage: java [-options] class [args...] (to execute a class) or java [-options] -jar jarfile [args...] (to execute a jar file) where options include: -client to select the "client" VM -server to select t…

阿里服务器+Centos7.4+Tomcat+JDK部署

适用对象 本文档介绍如何使用一台基本配置的云服务器 ECS 实例部署 Java web 项目。适用于刚开始使用阿里云进行建站的个人用户。 配置要求 这里列出的软件版本仅代表写作本文档使用的版本。操作时,请您以实际软件版本为准。 操作系统:CentOS 7.4Tomcat …

php输出mysqli查询出来的结果

php连接mysql我有文章已经写过了,这篇文章主要是介绍从mysql中查询出结果之后怎么输出的问题。 一:mysqli_fetch_row(); 查询结果:array([0]>小王) 查询: [php] view plaincopy while ($row mysqli_fetch_assoc($result)) …

rhel mysql安装_RHEL6.4下MySQL安装方法及简单配置

1.MySQL安装方法简介 1.rpm包yum安装 2.通用二进制包安装 3.源码编译安装 注意:实验所采用的系统平台为:RHEL6.4 2.rpm ins首页 → 数据库技术背景:阅读新闻RHEL6.4下MySQL安装方法及简单配置[日期:2014-04-08]来源:Li…

H.264算法的DSP移植与优化

摘要:在TMS320DM643平台上实现H.264基档次编码器的移植与优化显得格外实用和必要。基于对DSP平台的结构特性和H.264的计算复杂度分析,主要从核心算法、数据传输和存储器/Cache使用几方面对H.264编码器进行了…

IDA*与A*

我实在懒得写博客了,直接放上来之前讲课做的的PPT得了。 PPT_Source Code.zip 转载于:https://www.cnblogs.com/zzzc18/p/8323927.html

java 子类 父类 转换_Java子类与父类之间的类型转换

1.向上转换父类的引用变量指向子类变量时,子类对象向父类对象向上转换。从子类向父类的转换不需要什么限制,只需直接蒋子类实例赋值给父类变量即可,这也是Java中多态的实现机制。2.向下转换在父类变量调用子类特有的、不是从父类继承来的方法…

H.264视频编解码的代码移植和优化

基于DSP系统开发的视频编解码系统,国内几乎都是走的移植,优化的路线,并且移植的代码,都是开源的。毕竟花费大量的人力,物力去开发一套自己的代码,并不见得比一些成熟的开源代码效率更高,健壮性更…

SublimeText2 快捷键

SublimeText2 快捷键,与对应功能一览表: 快捷键功能ctrlshiftn打开新Sublimectrlshiftw关闭Sublime,关闭所有打开文件ctrlshiftt重新打开最近关闭文件ctrln新建文件ctrls保存ctrlshifts另存为ctrlf4关闭文件ctrlw关闭ctrlk, ctrlb切换侧边栏显…