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.pswp.cn/news/453644.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453644.shtml
英文地址,請注明出處:http://en.pswp.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切換側邊欄顯…