簡體   English   中英

將遞歸函數轉換為 for 循環和 while 循環

[英]Convert recursive function into a for loop and while loop

我需要轉換

def aRecursive(n):
    if n is 1:
        return 3
    else:
        return 2 * aRecursive(n-1) + 5

進入 for 循環和 while 循環。 我似乎無法理解這個過程。 這些循環的原始函數是:

a(1) = 3
a(n) = 2 * a(n-1) + 5

答案和解釋會大有幫助。

我將根據函數的調用方式提供一個解決方案。 此解決方案是通用的,因為您可以使用相同的方法將任何遞歸函數轉換為迭代函數。 這在理論上是可能的,但似乎沒有人談論如何。 由於您的函數很簡單,因此想出一個迭代函數並不難。 但是二叉樹的非遞歸后序遍歷呢? 如果您沒有我將要介紹的通用方法,則只能逐案進行。

開始了。 首先,我們需要寫下您的遞歸版本,稍作更改以方便轉換:

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
[start]:         # here we create a label; not python, only for converting
    if n == 1:
        return 3
    else:
        return 2 * f(n-1) + 5

接下來我們將轉換兩件事:函數調用和返回語句。 函數調用基本上分為兩步:將參數和返回地址推入堆棧並跳轉到實際代碼。 return的基本就是pop參數,跳轉到保存的地址。 所以我們開始:

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
[start]:         # here we create a label; not python, only for converting
    if n == 1:
        return 3
    else:
        push(n)       # push current parameter; since there is only one recursive function call in the body, we know where to return and there is no need to push the return address
        n = n-1       # the next level actual parameter is now n-1
        goto [start]  # we go to the code of the function which is the start of this same function
        return 2 * f(n-1) + 5  # we will never reach here... this line is where we need to return when any `return` statements is met

接下來,我們將更改第一個return語句( return 3 ):

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
[start]:         # here we create a label; not python, only for converting
    if n == 1:
        res = 3
        # for `return` we need to see if this is the top level or a inner recursive call
        if stack is empty: # we are in top level
            return res
        # hey we are in a recursive call, and we need to return to the code after `goto`, why not move these code here?
        else:
            n = pop()      # we pop the parameter saved
            # this line is where we need to return when any `return` statements is met
            return 2 * f(n-1) + 5
    else:
        push(n)       # push current parameter; since there is only one recursive function call in the body, we know where to return and there is no need to push the return address
        n = n-1       # the next level actual parameter is now n-1
        goto [start]  # we go to the code of the function which is the start of this same function

然后我們將轉換return 2*f(n-1)+5

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
[start]:         # here we create a label; not python, only for converting
    if n == 1:
        res = 3
        if stack is empty:
            return res
        else:
            [loop]:
            n = pop()      # we pop the parameter saved
            # begin conversion of return
            res = 2*res+5
            if stack is empty:
                return res;
            else:
                # we need to pop stack and jump to the same return, so we just jump to [loop]
                goto [loop]
    else:
        push(n)       # push current parameter; since there is only one recursive function call in the body, we know where to return and there is no need to push the return address
        n = n-1       # the next level actual parameter is now n-1
        goto [start]  # we go to the code of the function which is the start of this same function

現在轉換完成了,我們需要簡化這個爛攤子。 首先我們應該考慮是否真的需要一個堆棧。 對於這個特定的問題,每次推送都會生成n=n-1並且每次彈出都會生成n=n+1 所以堆棧並不是真正需要的。

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
[start]:         # here we create a label; not python, only for converting
    if n == 1:
        res = 3
        if n == N:         # SAME as stack is empty
            return res     # really return
        else:
            [loop]:
            n = n+1       # WE JUST INCREASE N INSTEAD OF POP
            res = 2*res+5
            if n==N:      # SAME as stack is empty
                return res;
            else:
                goto [loop]
    else:
        # NO PUSH NEEDED
        n = n-1       # the next level actual parameter is now n-1
        goto [start]

堆棧被淘汰了,我們需要讓這些goto語句消失。 注意[start]標簽和goto [start]構成了一個循環,我們只需要將它們變成一個 'while' 循環:

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
    # we reverse the if n==1 and make it a condition in while
    while n != 1:
        # NO PUSH NEEDED
        n = n-1       # the next level actual parameter is now n-1
    # you soon noticed the above calculation is not needed at all, it just sets n = 1

    res = 3
    if n == N:
        return res
    else:
        [loop]:
        n = n+1
        res = 2*res+5
        if n==N:
            return res;
        else:
            goto [loop]

我們優化了第一個循環並將它們替換為n=1 我們需要使標記為[loop]goto [loop]的第二個循環消失:

def f(N):
    n = N        # N is the top level parameter passed to this function
    res = None   # res is the result returned by this function
    n = 1        # the result of the first while loop

    res = 3
    if n == N:
        return res
    else:
        do:              # Python does not have do while, but it is straight forward to do this convert
            n = n+1
            res = 2*res+5
        while n!=N
        return res

我們很快就會注意到前 4 條語句可以合並,我們將刪除所有注釋:

def f(N):
    n = 1
    res = 3
    if n == N:
        return res
    else:
        do:
            n = n+1
            res = 2*res+5
        while n!=N
        return res

我們將反轉if n==N語句:

def f(N):
    n = 1
    res = 3
    if n != N:
        do:
            n = n+1
            res = 2*res+5
        while n!=N
        return res
    else:
        return res

很明顯, return res可以放在頂層,並且if n!=Ndo/while循環可以組合成一個while循環:

def f(N):
    n = 1
    res = 3
    while n != N:
        n = n+1
        res = 2*res+5
    return res

這是原始遞歸函數的等效版本。 請注意,我沒有深入研究這個特定問題來提出這個版本,我只處理函數調用轉換。 我建議你在你最喜歡的文本編輯器中自己完成整個過程,這很有趣。 你會發現它非常機械,唯一需要考慮的是堆棧的使用。 其他技術,如“如果條件反轉”或“將 goto 轉換為結構循環”,則非常簡單。

有趣的是,迭代版本比僅基於轉換過程的遞歸版本更有效,因為: 1. 我們消除了堆棧的使用; 2. 我們消除了將 n 減少到 1 的循環。我們至少節省了一些 CPU 周期和堆棧存儲。

一種可能的for循環:

def a(n):
    answer = 3

    for i in range(n - 1):
        answer = answer * 2 + 5

    return answer

一個可能的while循環,雖然我並不特別喜歡while這里使用while

def a(n):
    answer = 3

    while n > 1:
        answer = answer * 2 + 5
        n -= 1

    return answer

請注意,這些答案(或您的原始代碼)都沒有處理小於 1 的n

解釋

a(1) = 3
a(n) = 2 * a(n - 1) + 5

因此,如果您要計算a(5) ,則有兩種合理的方法。 一種是寫出遞歸的東西:

a(5) = 2 * a(4) + 5

然后計算a(4)

a(4) = 2 * a(3) + 5

所以a(5)現在是:

a(5) = 2 * (2 * a(3) + 5) + 5

你可以繼續這個過程,直到你不再有任何對a引用,然后你就可以做算術了。

非遞歸方式是向上計數:

a(1) = 3
a(2) = 2 * a(1) + 5  =  2 *  3 + 5  =  11
a(3) = 2 * a(2) + 5  =  2 * 11 + 5  =  27
a(4) = 2 * a(3) + 5  =  2 * 27 + 5  =  59
a(5) = 2 * a(4) + 5  =  2 * 59 + 5  =  123

這樣,您從 3 開始,然后在每個步驟中乘以 2 並加上 5 以獲得下一個數字。 當你達到你試圖計算其函數的n時就停止。

第二種(非遞歸)方法是上述forwhile循環的工作方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM