2015-10-19 106 views
3

我已經在C中編寫了下面的代碼。我們可以稱之爲尾遞歸實現嗎?Ackermann函數的這個實現可以稱爲尾遞歸嗎?

#include <stdio.h> 

int ackermann(unsigned int *m, unsigned int *n, unsigned int* a, int* len) 
{ 
    if(!*m && *len == -1) { 
    return ++*n; 
    } 
    else if(!*m && *len >= 0) { 
    ++*n; 
    *m = a[(*len)--]; 
    } 
    else if(*n == 0) { 
    --*m; 
    *n = 1; 
    } else { 
    ++*len; 
    a[*len] = *m - 1; 
    --*n; 
    } 
    return ackermann(m, n, a, len); 
} 

int main() 
{ 
    unsigned int m=4, n=1; 
    unsigned int a[66000]; 
    int len = -1; 

    for (m = 0; m <= 4; m++) 
    for (n = 0; n < 6 - m; n++) { 
     unsigned int i = m; 
     unsigned int j = n; 
     printf("A(%d, %d) = %d\n", m, n, ackermann(&i, &j, a, &len)); 
    } 

    return 0; 
} 

如果不是尾遞歸,請建議如何使它這樣。任何對Ackermann尾遞歸版本的引用在C/C++/Java或非函數式編程語言中都會很好。

+0

如果問題解決了問題,請將相應的答案標記爲_solution_,這樣未來的讀者可以輕鬆識別解決方案。 –

回答

1

通過definitionackermann功能尾遞歸函數爲你直接返回遞歸情況下的結果。由於沒有進一步的邏輯取決於遞歸調用的結果,因此編譯器可以安全地應用尾遞歸優化。

+0

感謝您的確認。 – user902384

+0

沒問題:)爲了利用這個優化,您的榮譽。由於遞歸調用而創建的額外堆棧幀非常昂貴。 –

3

你的函數使用數據結構做回溯,所以雖然它是一個尾遞歸函數,但它絕對不是一個簡單的遞歸或迭代過程。數組a擔當遞歸堆棧的角色。你可以一起寫出遞歸調用:

int ackermann(unsigned int *m, unsigned int *n, unsigned int* a, int* len) 
{ 
    while (*m || *len != -1) { 
     if(!*m && *len >= 0) { 
     *n++; 
     *m = a[(*len)--]; 
     } else if(*n == 0) { 
     *m--; 
     *n = 1; 
     } else { 
     ++*len; 
     a[*len] = *m - 1; 
     *n--; 
     } 
    } 
    return ++*n; 
} 

仍然沒有任何遞歸調用,我認爲這是一個遞歸過程。

+0

[通過模擬遞歸堆棧(通常在堆上 - 每個遞歸可以轉換爲迭代](http://stackoverflow.com/questions/931762/can-every-recursion-be-converted-into-iteration)問題就變成了這個空間的大小)。 - 'unsigned int a [66000];'看起來可以用來模擬遞歸堆棧。當然對於更大的'n'和'm',如果它是一個適當的阿克曼函數,那麼保留空間必須增大得多。 –

+0

以及使用的增長空間與完成計算所需的時間相對應。這是我認爲這個功能的重點 - 它的增長非常快*很快。正如對這個問題的評論所說的那樣,這種轉換是關於「將堆棧變量轉換成(模擬)堆棧變量」。 :) –

+0

@WillNess你說的是遞歸函數與迭代函數,而我正在談論遞歸過程與迭代過程。如果你做了一個迭代循環,並增長一個數據結構來做某種回溯,它仍然是一個遞歸過程*。同樣,如果您在TCO語言中使用尾遞歸,即使使用遞歸,也是一個迭代過程。可迭代的遞歸,如斐波那契,變得迭代。阿克曼斯功能不會。 – Sylwester