Ackerman 函数的解法

Ackerman 函数的解法
1.定义

    ack(m,n) =  n+1                     m = 0
    ack(m,n) = ack(m-1,1)            m!=0  n = 0
    ack(m,n) = ack(m-1,n-1)         m!=0  n!=0

2.示例
     ack(3,0) = (2,1) 
                 = (1,(2,0))
                 = (1,( 1,1))
                 = (1,(0,(1,0)))
                 = (1,(0,(0,1))
                 = (1,(0,2))
                 = (1,3)
                 = (0,(1,2))
                 =(0,(0,( 1,1))
                 ...
                 =(0,(0,3))
                 ...
                 = 5  

3.复杂性分析
 待解决,m>5后复杂度极高。


4.最简单的递归解法
//按照函数的递归定义即可

    int ack(int m, int n)
    {
         if (m == 0)
          return n+1;

         if (n == 0)
          return ack(m-1, 1);

         return ack(m-1, ack(m,n-1));

    }

5.去掉递归,用栈保存信息
/*
 *  n = 0 的时候往下递归其实只有一个递归分支,无需保留信息,可以用循环取代的
 *  而 对于 m!=0 && n!=0 的情况 注意到是一个迭代递归
 *  这其中 注意 我们用到 ack(m,n-1)的返回值作为 ack(m-1,x)的值
 *  事实上只需要m入栈,使得我们在进入到 ack(m,n-1)后最后能够返回出来计算 ack(m-1,x) x = ack(m,n-1)
 */

下面给出 带 goto 和不带 goto 语句的两种解法
int ack_goto(int m, int n)
{
int result;
stack<int> stk;

start:
if (m == 0)
{
result = n+1;
if (stk.empty())
{
goto end;
else
{
goto qiantao;
}
}
else if (n == 0)
{
m = m-1;
n = 1;
goto start;
}
else
{
m = m;
n = n-1;
stk.push(m);   //为了保留当期信息,只需保留m等待 右面嵌套返回结果继续
goto start;
qiantao:
m = stk.top();
stk.pop();
m = m-1;
n = result;
goto start;
}


end:
return result;
}



//机械式的对递归程序的用栈标准的非递归翻译
int ack_norec(int m, int n)
{
stack<int> stk;

stk.push(m);

while (!stk.empty())
{
m = stk.top();
stk.pop();
if (m == 0)
{
n = n+1;

}
else if (n == 0)
{
m = m-1;
n = 1;
stk.push(m);
}
else
{
m = m;
n = n-1;
stk.push(m-1);
stk.push(m);
}
}

return n;

}

5.对于以上基于定义用递归或是用栈消除递归的做法,有没有可能优化呢?
   对于示例 ack(3,0) 可以注意到 ack(1,1)出现了两次,对于上面的解法,ack(1,1)也就被计算了两次。
   事实上我们可以记录已经做好的中间结果,避免重复的递归,也就是所谓的剪枝。

    5.1递归加剪枝
        const int mMax = 5;
        const int nMax = 1000000;
        int ackV[mMax][nMax];
        bool got[mMax][nMax];       //注意全部初始为false

        int ack2(int m, int n)
        {
    if (m > mMax || n > nMax)
    {
cout << "error input too large" << endl;
exit(1);
    }
    if (got[m][n] == true)
return ackV[m][n];

    if (m == 0)
return n+1;
    if (n == 0 )
return ack2(m-1,1);

    ackV[m][n] = ack2(m-1,ack2(m,n-1));
    got[m][n] = true;
    return ackV[m][n];
        }
     
    5.2非递归算法的优化
         int ack_norec2(int m, int n)
        {
            if (m > mMax || n > nMax)
    {
cout << "error input too large" << endl;
exit(1);
    }

    stack<int> stk;
    stack<int> stk2;
    int result;
    bool flag = 0;

    stk.push(m);

    while (!stk.empty())
    {
                     if (n > nMax)
            {
        cout << "error input too large" << endl;
        exit(1);
            }
    m = stk.top();
    stk.pop();
   
    if (flag == 1)
    {
    if (got[m+1][stk2.top()] == false)
    {
        ackV[m+1][stk2.top()] = n;
    got[m+1][stk2.top()] = true;
    }
    stk2.pop();
    flag = 0;
    }
    if (got[m][n] == true)
    {
    n = ackV[m][n];   //退出口
            flag = 1;          //尽管不需要记录这个结果了但因为n已经被Push了要出栈
    continue;
    }

    if (m == 0)
    {
    n = n+1;          //退出口
    flag = 1;

    }
    else if (n == 0)
    {
    m = m-1;
    n = 1;
    stk.push(m);
    }
    else
    {
    m = m;
    n = n-1;
    stk.push(m-1);
    stk2.push(n);
    stk.push(m);
    }
        }

    return n;
        }

    
6.动态规划,记录前面的结果,空间换时间


   

你可能感兴趣的:(职场,休闲)