[英]How to convert recursive to iterative solution
我設法以遞歸的方式編寫算法:
int fib(int n) {
if(n == 1)
return 3
elseif (n == 2)
return 2
else
return fib(n – 2) + fib(n – 1)
}
目前,我正在嘗試將其轉換為迭代方法,但未成功:
int fib(int n) {
int i = 0, j = 1, k, t;
for (k = 1; k <= n; ++k)
{
if(n == 1) {
j = 3;
}
else if(n == 2) {
j = 2;
}
else {
t = i + j;
i = j;
j = t;
}
}
return j;
}
那么,如何糾正我的代碼以實現自己的目標?
通過一般的轉換為迭代來解決此問題是一個壞主意。 但是,這就是您的要求。
這些都不是解決fib
好方法:沒有針對fib
封閉式解決方案,和/或更簡潔的迭代解決方案,和/或遞歸的固定解決方案。 相反,我正在展示相對機械的技術,以采用遞歸函數(不是尾遞歸或其他易於解決的方法),並在不使用自動存儲堆棧的情況下解決該問題(遞歸)。
我的代碼對遞歸嵌套的處理太深,在中高復雜性的情況下會破壞堆棧。 當重構為迭代時,問題就消失了。 這些是在當你擁有的是一個遞歸的解決方案,你懂的一半所需的解決方案,你需要它是重復的。
將遞歸轉換為迭代解決方案的一般方法是手動管理堆棧。
在這種情況下,我還將記住返回值。
我們將返回值緩存在retvals
。
如果我們不能立即解決問題,則說明要解決問題首先需要解決的問題(特別是n-1和n-2情況)。 然后,我們再次排隊解決問題(到那時,我們將准備好所需的東西)。
int fib( int n ) {
std::map< int, int > retvals {
{1,3},
{2,2}
};
std::vector<int> arg;
arg.push_back(n);
while( !arg.empty() ) {
int n = arg.back();
arg.pop_back();
// have we solved this already? If so, stop.
if (retvals.count(n)>0)
continue;
// are we done? If so, calculate the result:
if (retvals.count(n-1)>0 && retvals.count(n-2)>0) {
retvals[n] = retvals[n-1] + retvals[n-2];
continue;
}
// to calculate n, first calculate n-1 and n-2:
arg.push_back(n); arg.push_back(n-1); arg.push_back(n-2);
}
return retvals[n];
}
沒有遞歸,只是一個循環。
一種“愚蠢”的方法是使用該函數並將其設為偽協程。
首先,重寫您的遞歸代碼以每行執行一件事:
int fib(int n) {
if(n == 1)
return 3
if (n == 2)
return 2
int a = fib(n-2);
int b = fib(n-1);
return a+b;
}
接下來,創建一個具有所有函數狀態的結構:
struct fib_data {
int n, a, b, r;
};
並在我們進行遞歸調用的每個點處添加標簽,並添加一個具有類似名稱的枚舉:
enum Calls {
e1, e2
};
int fib(int n) {
fib_data d;
d.n = n;
if(d.n == 1)
return 3
if (d.n == 2)
return 2
d.a = fib(n-2);
CALL1:
d.b = fib(n-1);
CALL2:
d.r = d.a+d.b;
return d.r;
}
將CALLS
添加到您的fib_data
。
接下來創建一個fib_data
堆棧:
enum Calls {
e0, e1, e2
};
struct fib_data {
Calls loc = Calls::e0;
int n, a, b, r;
};
int fib(int n) {
std::vector<fib_data> stack;
stack.push_back({n});
if(stack.back().n == 1)
return 3
if (stack.back().n == 2)
return 2
stack.back().a = fib(stack.back().n-2);
CALL1:
stack.back().b = fib(stack.back().n-1);
CALL2:
stack.back().r = stack.back().a + stack.back().b;
return stack.back().r;
}
現在創建一個循環。 取而代之的遞歸調用,在你設定的返回地點fib_data
,推fib_data
到堆棧與n
和e0
的位置,然后繼續循環。 在循環的頂部,切換到堆棧位置的頂部。
要返回:創建一個函數局部變量r來存儲返回值。 要返回,請設置r
,彈出堆棧,然后繼續循環。
如果循環開始時堆棧為空,則從函數返回r
。
enum Calls {
e0, e1, e2
};
struct fib_data {
int n, a, b, r;
Calls loc = Calls::e0;
};
int fib(int n) {
std::vector<fib_data> stack;
stack.push_back({n});
int r;
while (!stack.empty()) {
switch(stack.back().loc) {
case e0: break;
case e1: goto CALL1;
case e2: goto CALL2;
};
if(stack.back().n == 1) {
r = 3;
stack.pop_back();
continue;
}
if (stack.back().n == 2){
r = 2;
stack.pop_back();
continue;
}
stack.back().loc = e1;
stack.push_back({stack.back().n-2});
continue;
CALL1:
stack.back().a = r;
stack.back().loc = e2;
stack.push_back({stack.back().n-1});
continue;
CALL2:
stack.back().b = r;
stack.back().r = stack.back().a + stack.back().b;
r = stack.back().r;
stack.pop_back();
continue;
}
}
然后注意b
和r
不必在堆棧中-將其刪除並放在本地。
這種“笨拙”的轉換模擬了遞歸時C ++編譯器的工作,但是堆棧存儲在免費存儲中,而不是自動存儲中,並且可以重新分配。
如果需要保留指向局部變量的指針,則對堆棧使用std::vector
無效。 將指針替換為標准向量中的偏移量,它將起作用。
應為fib(0)= 0,fib(1)= 1,fib(2)= 1,fib(3)= 2,fib(4)= 3,fib(5)= 5,fib(6)= 8,...。
fib(n)
{
int f0, f1, t;
if(n < 2)
return n;
n -= 2;
f0 = 1;
f1 = 1;
while(n--){
t = f1+f0;
f0 = f1;
f1 = t;
}
return f1;
}
或者您可以稍微展開循環,然后刪除temp變量:
int fib(int n)
{
int f0, f1;
if(n < 2)
return n;
f0 = 1-(n&1);
f1 = 1;
while(0 < (n -= 2)){
f0 += f1;
f1 += f0;
}
return f1;
}
這是一個經典的問題。 如果給定n並想要向下計算,則不能簡單地擺脫遞歸。
解決方案是動態編程。 基本上,您想創建一個大小為n的數組,然后從索引0開始填充它直到達到索引n-1;
像這樣的東西:
int fib(int n)
{
int buffer[n+1];
buffer[0]=3;
buffer[1]=2;
for(int i=2;i<=n; ++i)
{
buffer[i] = buffer[i-1] + buffer[i-2];
}
return buffer[n];
}
或者,為了節省內存而不使用大數組,可以使用:
int fib(int n)
{
int buffer [2];
buffer[0] = 3;
buffer[1] = 2;
for(int i=3; i<=n; i++)
{
int tmp = buffer[0] + buffer[1];
buffer[0] = buffer[1];
buffer[1] = temp;
}
return buffer[1];
}
為了完整起見,這里是O(1)空間復雜度的迭代解決方案:
int fib(n)
{
int i;
int a0 = 3;
int a1 = 2;
int tmp;
if (n == 1)
return a0;
for (i = 3; i <=n; i++ )
{
tmp = a0 + a1;
a0 = a1;
a1 = tmp;
}
return a1;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.