• 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,ack(m,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.动态规划,记录前面的结果,空间换时间


       

  • 相关阅读:
    JAVA 正则表达式 (超详细)
    <select>改造成<s:select>实现表单的回显功能
    打开新界面
    list删除操作 java.util.ConcurrentModificationException
    C# 增加 删除 更新 方法
    C# 网页内容获取
    excel 处理方法
    C# 读取excel
    sql 导入excel 遇到问题
    DataSet
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1441313.html
Copyright © 2020-2023  润新知