python遞歸函數詳解-講解Python中的遞歸函數

在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。

舉個例子,我們來計算階乘n! = 1 x 2 x 3 x ... x n,用函數fact(n)表示,可以看出:

?

1

fact(n)= n!= 1 x2 x3 x ... x (n-1) x n= (n-1)! x n= fact(n-1) x n

所以,fact(n)可以表示為n x fact(n-1),只有n=1時需要特殊處理。

于是,fact(n)用遞歸的方式寫出來就是:

?

1

2

3

4

def fact(n):

if n==1:

return 1

return n* fact(n- 1)

上面就是一個遞歸函數。可以試試:

?

1

2

3

4

5

6

>>> fact(1)

1

>>> fact(5)

120

>>> fact(100)

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

如果我們計算fact(5),可以根據函數定義看到計算過程如下:

?

1

2

3

4

5

6

7

8

9

10

===> fact(5)

===>5 * fact(4)

===>5 * (4 * fact(3))

===>5 * (4 * (3 * fact(2)))

===>5 * (4 * (3 * (2 * fact(1))))

===>5 * (4 * (3 * (2 * 1)))

===>5 * (4 * (3 * 2))

===>5 * (4 * 6)

===>5 * 24

===>120

遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。

使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試fact(1000):

?

1

2

3

4

5

6

7

>>> fact(1000)

Traceback (most recent call last):

File "", line1,in

File "", line4,in fact

...

File "", line4,in fact

RuntimeError: maximum recursion depth exceeded

解決遞歸調用棧溢出的方法是通過尾遞歸優化,事實上尾遞歸和循環的效果是一樣的,所以,把循環看成是一種特殊的尾遞歸函數也是可以的。

尾遞歸是指,在函數返回的時候,調用自身本身,并且,return語句不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況。

上面的fact(n)函數由于return n * fact(n - 1)引入了乘法表達式,所以就不是尾遞歸了。要改成尾遞歸方式,需要多一點代碼,主要是要把每一步的乘積傳入到遞歸函數中:

?

1

2

3

4

5

6

7

def fact(n):

return fact_iter(1,1, n)

def fact_iter(product, count,max):

if count >max:

return product

return fact_iter(product* count, count+ 1,max)

可以看到,return fact_iter(product * count, count + 1, max)僅返回遞歸函數本身,product * count和count + 1在函數調用前就會被計算,不影響函數調用。

fact(5)對應的fact_iter(1, 1, 5)的調用如下:

?

1

2

3

4

5

6

7

===> fact_iter(1,1,5)

===> fact_iter(1,2,5)

===> fact_iter(2,3,5)

===> fact_iter(6,4,5)

===> fact_iter(24,5,5)

===> fact_iter(120,6,5)

===>120

尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出。

遺憾的是,大多數編程語言沒有針對尾遞歸做優化,Python解釋器也沒有做優化,所以,即使把上面的fact(n)函數改成尾遞歸方式,也會導致棧溢出。

有一個針對尾遞歸優化的decorator,可以參考源碼:

http://code.activestate.com/recipes/474088-tail-call-optimization-decorator/

我們后面會講到如何編寫decorator。現在,只需要使用這個@tail_call_optimized,就可以順利計算出fact(1000):

?

1

2

>>> fact(1000)

402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

小結

使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導致棧溢出。

針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能通過尾遞歸實現循環。

Python標準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534204.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534204.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534204.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php 高效判斷是否登錄,php 判斷用戶是否登錄

判斷用戶登陸主要分幾個過程,第一步是用戶登陸成功后把數據保存到session中,然后當用戶訪問需要登陸過的用戶權限時就來判斷session是否為空,如果不是就登錄成功。下面來看個實例session_start();if(getconfig("chatroom_admin")$_…

php異步處理任務工具,php異步任務處理: gearman

Gearman是一個用來把工作委派給其他機器、分布式的調用更適合做某項工作的機器、并發的做某項工作在多個調用間做負載均衡準備軟件包gearmand-1.1.12.tar.gzgearman-1.1.1.tgz php擴展安裝支持組件yum -y install boost-devel* gperf* libevent-devel* libuuid-devel*./configu…

頁面錯誤!請稍后再試_微信內嵌H5頁面授權和分享

近期新上線項目,用到了微信授權獲取用戶信息和分享,掉坑無數次,遂寫此篇,為后人指路項目情況技術選型項目語言:HTML、CSS、JavaScript項目框架:Vue.js項目搭建腳手架:vue-cli工程化工具&#xf…

電腦打字手指正確姿勢_正確的彈琴手型,應該是怎樣的?

手型是基礎,手型規范才有助于練習出正確的指法,指法正確就可以提高練習質量。剛入門的時候,不能刻意的要求手型,但是我們要有一個基本的要求,就是手要保持放松。彈琴的時候,沒有多余的身體的力量參與到觸鍵…

php 開源 采集,迅睿CMS 火車頭內容采集

采集工具:火車采集器 v7.6采集模塊:新聞 News一、編寫采集入庫腳本接口新建:./api/caiji.php/*** 數據采集*/define(IS_API, basename(__FILE__, .php)); // 項目標識define(SELF, pathinfo(__FILE__, PATHINFO_BASENAME)); // 該文件的名稱r…

英文數據集txt_YOLOv5在建筑工地中安全帽佩戴檢測的應用(已開源+數據集)

點擊上方“計算機視覺cv”即可“進入公眾號”重磅干貨第一時間送達前言隨著人工智能的發展,現在越來越多的場景需要人工智能。在工廠的廠區中以安全為首,但工人普遍缺乏佩戴安全帽意識;工廠環境復雜,有各種各樣的禁止進入的區域&a…

浪潮集團PHP,浪潮php實習第一天(初識php)

先推薦幾個比較好的php初學者資料php manual.chm(可在csdn下載頻道下載,可查到大部分函數)比較好的開發工具1 eclipse PDT(對eclipse比較熟悉的比較適應)2.zendStudio(公認的最好的php集成開發環境)php沒有想象中的那么難學,不僅僅是它的語法&#xff0c…

檢測到目標url存在內部ip地址泄露_Cendertron,動態爬蟲與敏感信息泄露檢測

Cendertron,動態爬蟲與敏感信息泄露檢測Cendertron Crawler RendertronCendertron https://url.wx-coder.cn/HinPM 是基于 Puppeteer 的 Web 2.0 動態爬蟲與敏感信息泄露檢測工具。其依托于 xe-crawler 的通用爬蟲、調度與緩存模型,新增了 Monkey Test…

wamp2 php配置,wamp安裝后自定義配置的方法

WampServer是目前應用非常廣泛的PHP集成開發環境,本文就來講述Wamp安裝后自定義配置的方法。供大家參考借鑒。具體如下:wamp2.5安裝完畢后,自己手動重新設置了apache的默認根目錄。但是發現本機可以訪問,別人不能訪問。提示信息為…

屏幕壞點檢測圖片_iPhone新機如何檢測質量 iPhone新機檢測質量步驟【詳解】

iPhone新機怎么檢測好壞_iPhone新機檢測質量方法 說實話,蘋果對于iPhone的品控把握確實一代不如一代,特別是去年發布的iPhone7系列,很多用戶都反映自己新買的手機存在劃痕、屏幕發黃、掉漆等問題。那么當我們購買一部全新的iPhone7時&#xf…

php 安全mysql,關于php:我從mysql注入安全嗎?

本問題已經有最佳答案,請猛點這里訪問。這是否足以避免SQL注入?這里只需要mysql_real_escape_string()方法。在將數據插入數據庫之前,不應該執行htmlentities()或urlencode()。這些方法通常是在呈現您提供給用戶的視圖期間編寫的代碼。避免SQ…

excel單元格斜線_怎么在excel中畫斜線?怎么在excel表格中畫斜線?

在excel表格中畫斜線的技巧教程:1.在Excel中打開一個空白工作簿。 2.您可以在任何大小的單元格中執行此操作,但是如果先將其增大則更容易理解。為此,我們只需單擊并按住第1行和第2行之間的線,然后將其拖動到所需的高度即可。然后對…

php報表數據打印機,通過打印機打印帶打印功能的php表

我有以下php表,我如何只在php表中添加打印功能?點擊一個按鈕,下面的表格通過打印機打印,我試過’CTRL P’,我只得到頁面的html部分示例頁眉,頁腳,導航欄,而不是結果php結果echo "FILEIDFirstnameLastnameIssue DateInterest RateTermsBalance OutstandingBalan…

xbox one s驅動_理想照進現實 理想ONE開始接受預定

2016年4月22日,車和家創始人&CEO李想在源碼資本第二屆碼會年會首談車和家,改造城市出行。車和家 創始人&CEO 李 想2016年碼會年會演講2019年4月10日,增程式智能電動車「理想ONE」正式公布售價并開始接受預訂,并將于2019年…

two+few+arguments+php,PHP5.5 ~ PHP7.2 新特性整理

PHP5.5 ~ PHP7.2 新特性整理一、從PHP 5.5.x 移植到 PHP 5.6.x使用表達式定義常量在之前的 PHP 版本中, 必須使用靜態值來定義常量,聲明屬性以及指定函數參數默認值。 現在你可以使用包括數值、字符串字面量以及其他常量在內的數值表達式來 定義常量、聲…

date設置時間_解決 IDEA 無法找到 java.util.Date 的問題

原文首發于 https://studyidea.cn/問題最近在項目中頻繁使用到 java.util.Date,但是使用 IDEA 提示查找 Date 類,卻無法找到 java.util.Date。可以看到,智能提示的結果沒有 java.util.Date。沒辦法,只能暫時手動導入該包。最近閑下…

mysql插入語句例句,一句簡單的MySql插入語句怎么寫 ?

守候你守候我insert into 表名 values(default,"名字","2011-04-15 12:22:25"); //default可以換成null------------------------------insert into 表名 (name,datetime) values("名字","2011-04-15 12:22:25");使用SQL語法大寫&…

vue key重復_【第2112期】 import { reactive } from #39;vue#39;

前言今日早讀文章由Anthony Fu授權分享。Anthony Fu,是 Vue 的 Core Team 的一員,在 Vue主要負責 vue/composition-api 這個項目的維護。這是一個面向 Vue 2 的插件,它在 Vue 2 中增加了 Vue 3 的 Composition API 的支持。最近也加入了 Vite…

matlab系統穩定性分析,控制系統穩定性分析的MATLAB實現

收稿日期 :200706220 基金項目 :周口師范學院青年基金資助項目(No. ZKNUQN200621) 作者簡介 :劉  偉(1976 - ) ,女 ,河南太康人 ,助教 ,碩士 ,主要從事電力系統及其自動化仿真研究. 第 25 卷 第 2 期 周口師范學院學報 2008 年 3 月 Vol. 25 No. 2 Journal of Zhoukou Normal …

路由器下一跳地址怎么判斷_網絡基本功三:細說路由器

介紹以太網交換機工作在第二層即數據鏈路層,用于在同一網絡內部轉發以太網幀。但是,當源和目的IP地址位于不同網絡時,以太網幀必須發送給路由器。路由器負責在不同網絡間傳輸報文,通過路由表來決定最佳轉發路徑。當主機將報文發送…