Ackerman 函数的解法

本文详细介绍了Ackerman函数的定义及解法,包括递归解法、去除递归使用栈的方法,并探讨了如何通过记录中间结果来优化算法,避免重复计算,提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.动态规划,记录前面的结果,空间换时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值