Python函數學習——遞歸


遞歸函數

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

函數實現過程

def calc(n):
    v = int(n//2)
    print(v)
    if v > 0:
        calc(v)
    print(n)

calc(10)

輸出結果

5
2
1
0
1
2
5
10

為什么是這個結果

遞歸特性:

  1. 必須有一個明確的結束條件
  2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
  3. 一般通過return結束遞歸
  4. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出)
  5. 堆棧掃盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html

遞歸深度

python默認對最大遞歸層數做了一個限制:997,但是也可以自己限制

import sys
sys.setrecursionlimit(10000)#修改遞歸層數
n=0
def f():
    global n
    n+=1
    print(n)
    f()
f()

 

遞歸應用

1.下面我們來猜一下小明的年齡

小明是新來的同學,麗麗問他多少歲了。

他說:我不告訴你,但是我比滔滔大兩歲。

滔滔說:我也不告訴你,我比曉曉大兩歲

曉曉說:我也不告訴你,我比小星大兩歲

小星也沒有告訴他說:我比小華大兩歲

最后小華說,我告訴你,我今年18歲了

這個怎么辦呢?當然,有人會說,這個很簡單啊,知道小華的,就會知道小星的,知道小星的就會知道曉曉的,以此類推,就會知道小明的年齡啦。這個過程已經非常接近遞歸的思想了。

用遞歸實現

"""
age(5) = age(4)+2
age(4) = age(3) + 2 
age(3) = age(2) + 2
age(2) = age(1) + 2
age(1) = 18
"""

def calc_age(n):
    if n == 1:
        return 18
    else:
        return calc_age(n-1)+2

print(calc_age(5)) # 26

 

2.一個數,除2直到不能整除2
n = 100
def cal(n):
    if n == 0:
        return
    else:
        n = int(n // 2)
        print(n)
        cal(n)
        print("退出=", n)
cal(100)
3.一個數,除2直到次數等於5退出
def calc(n,count):
    print(n, count)
    if count < 5:
        r = calc(n / 2, count + 1)
        return r  # 里層返回為上層,此處不加return 返回None
    else:
        return n # 最里層返回

res = calc(188, 1)
print('res ', res)

遞歸調用過程

4.深度查詢
menus = [
    {
        'text': '北京',
        'children': [
            {'text': '朝陽', 'children': []},
            {'text': '昌平', 'children': [
                {'text': '沙河', 'children': []},
                {'text': '回龍觀', 'children': []},
            ]},
        ]
    },
    {
        'text': '上海',
        'children': [
            {'text': '寶山', 'children': []},
            {'text': '金山', 'children': []},
        ]
    }
]
#  深度查詢
#1. 打印所有的節點
#2. 輸入一個節點名字,沙河, 你要遍歷找,找到了,就打印它,並返回true,

實現

# 打印所有的節點
def recu_Menu(menu):
    for sub_menu in menu:
        menu_text = sub_menu['text']
        menu_children = sub_menu['children']
        print(menu_text)
        recu_Menu(menu_children)

recu_Menu(menus)


# 打印所有的節點,輸入一個節點名字,沙河, 你要遍歷找,找到了,就打印它,並返回true,
def recu_Menu_node(menu, node, layer):
    # if len(menu)>0:
    for sub_menu in menu:
        menu_text = sub_menu['text']
        menu_children = sub_menu['children']
        print("menu_text=", menu_text)
        if node == menu_text:
            print("找到%s在第%s層" % (node, layer)) #返回到外層 return True
        else:
            if recu_Menu_node(menu_children, node, layer + 1) == True: #如果里層返回True,繼續向上返回True return True
            else:
                recu_Menu_node(menu_children, node, layer + 1)
node_str = input("輸入一個節點名字-> ")
print(recu_Menu_node(menus, node_str, 1))

-》回龍觀

找到回龍觀在第3層
True

 

5.猴子吃桃問題
# 題目:猴子吃桃問題:猴子第一天摘下若干個桃子,當即吃了一半,還不癮,又多吃了一個
#  第二天早上又將剩下的桃子吃掉一半,又多吃了一個。
#  以后每天早上都吃了前一天剩下的一半零一個。
#  到第10天早上想再吃時,見只剩下一個桃子了。求第一天共摘了多少。

"""
下一天等於是前一天吃了一半還多一個剩下的。
所以f(n) = 2 * f(n - 1) + 2
"""
def peach(n):
    if n == 1:
        return 1
    else:
        return 2 * peach(n-1) + 2

print(peach(10))  # 1534
6.二分查找算法

從[1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]序列中找到30的位置

 

 

 代碼實現

data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
print('start to find')

# 遞歸二分查找
def binary_search(dataset, start, end, val):
    mid = int((start + end)/ 2)  # 取中間數
    # print(dataset, mid, start, end)
    if start <= end:
        if dataset[mid] == val:  # 判斷中間值和要找的那個值的大小關系
            print("find val", dataset[mid])
            return mid
        elif dataset[mid] > val:
            print('mid %s is bigger than %s, keep looking in left %s' % (dataset[mid], val, mid))
            return binary_search(dataset, start, mid-1, val)
        else:  # dataset[mid] < val:
            print('mid %s is smaller than %s, keep looking in right %s' % (dataset[mid], val, mid))
            return binary_search(dataset, mid+1, end, val)
    else:
        # if dataset[start] == val:
        #     print('finally find val:', dataset[start])
        #     return start
        # else:
            print("data %s doesn't exist in dataset " % val)
            return -1

print('start to find')
print(binary_search(data,0,len(data)-1, 30))

輸出結果

start to find
mid 17 is smaller than 30, keep looking in right 8
mid 23 is smaller than 30, keep looking in right 13
mid 32 is bigger than 30, keep looking in left 15
find val 30
mid =14 #返回位置為14

 

另一種實現

data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
print('start to find')
def binary_search(dataset, val):
    mid = int(len(dataset)/ 2)  # 取中間數
    print(dataset)
    if mid > 0:
        if dataset[mid] == val:  # 判斷中間值和要找的那個值的大小關系
            print("find n", dataset[mid])
        elif dataset[mid] > val:
            new_dataset = dataset[:mid]  # 顧頭不顧尾
            print('mid %s is bigger than %s, keep looking in left %s' % (dataset[mid], val, mid))
            binary_search(new_dataset, val)
        else: # dataset[mid] < val:
            new_dataset = dataset[mid:]  # 顧頭不顧尾
            print('mid %s is smaller than %s, keep looking in right %s' % (dataset[mid], val, mid))
            binary_search(new_dataset, val)
    else:
        if dataset[0] == val:
            print('finally find val:', dataset[0])
        else:
            print("data %s doesn't exist in dataset " % val)

binary_search(data,30)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM