要求

已知akm函数如下:

{ n+1 while m=0 }                          => Rule I

akm(m,n)= { akm(m-1,1) while n=0 }                => Rule II

{ akm(m-1,akm(m,n-1)) otherwise } => Rule III

写出递归与非递归算法,并输出调用过程。

实现

参见https://github.com/bajdcc/ALGImplements/blob/master/akm/akm.cpp

#include "stdafx.h"
#include <stdio.h>
#include <stack> /*
* Ackerman function definition
*
* { n+1 while m=0 } => Rule I
* akm(m,n)= { akm(m-1,1) while n=0 } => Rule II
* { akm(m-1,akm(m,n-1)) otherwise } => Rule III
*/ //************************************
// Method: akm1
// FullName: 递归解法
// Access: public
// Returns: int
// Qualifier: recursion
// Parameter: int m
// Parameter: int n
//************************************
int akm1(int m, int n)
{
printf("Compute akm(%d,%d)\n", m, n);
if (m == )
{
printf("Compute akm(%d,%d) - val = %d\n", m, n, n + );
return n + ;
}
else if (n == )
{
return akm1(m - , );
}
else
{
return akm1(m - , akm1(m, n - ));
}
} //************************************
// Method: akm2
// FullName: 非递归解法一
// Access: public
// Returns: int
// Qualifier: non-recursion
// Parameter: int m
// Parameter: int n
//************************************
int akm2(int m, int n)
{
/* 这个解法有其特殊性,因为递归调用数为一,没有并列调用(一个以上参数为递归调用,如akm(akm(...),akm(...))) */
//这样,在栈中,调用关系为线性关系,而不是树形关系 //栈的惰性求值方法,若表达式中含有非求值akm函数,则记录它在链表中的位置
//由于存在m和n参数以及akm函数返回值,因此一个结点中有m,n,val
struct akm_node
{
int m, n, val;
};
std::stack<akm_node> exp_stack;
akm_node initial_val;//初始值
initial_val.m = m;
initial_val.n = n;
initial_val.val = -;//返回值尚未求得
exp_stack.push(initial_val);
/* 注意:这个方法假设akm的值为正数 */
while (true)//主循环
{
akm_node& node = exp_stack.top();//当前处理的表达式,取栈顶且不出栈,取引用说明要修改
printf("Compute akm(%d,%d)\n", node.m, node.n);
//为什么为top不急着出栈,这是因为之前的表达式要用到当前值
//此时出栈后,之前的式子就没有获得返回值
//出栈的最佳时机,即为式子取得其参数(返回值)后
if (node.val != -)//值被求出
{
//什么时候才是出栈的时机,这个时候!
//此时,值已经求得,即为可解问题
//我们知道可解问题一般为树的叶子结点
//求得这个值之后,可能这个结果是上一次调用的参数
//那么应该亡羊补牢,赶紧补上上一次调用的参数
exp_stack.pop();//值在node中,故可出栈
if (exp_stack.empty())//Rule I
{
//假使当前栈空,那么这个node就是最终值(所有嵌套全部计算完毕),九九归一
return node.val;
}
else
{
//栈不空,说明【肯定】含有待解问题
//因为当且仅当栈空时,调用栈为空,即树归约到根结点,得到最终结果
akm_node& not_computed_exp = exp_stack.top();//取引用说明要修改
if (not_computed_exp.n == )//Rule II called Rule I
{
not_computed_exp.val = node.val;//give it computed value
}
else if (not_computed_exp.n == -)//Rule III called Rule I
{
not_computed_exp.n = node.val;
}
else
{
not_computed_exp.val = node.val;
}
}
continue;
}
if (node.m == )//Rule I: m+1 while m equals to zero
{
printf("Compute akm(%d,%d) - val = %d\n", node.m, node.n, node.n + );
node.val = node.n + ;
}
else if (node.n == )//Rule II akm(m-1,1) while n equals to zero
{
//此时值不可直接求解,故需要嵌套求值(惰性求值)
//将二次调用参数进栈
akm_node secondary_call_exp;
secondary_call_exp.m = node.m - ;
secondary_call_exp.n = ;
secondary_call_exp.val = -;
exp_stack.push(secondary_call_exp);
}
else//Rule III akm(m-1,akm(m,n-1)) otherwise
{
//此时值不可直接求解,故需要嵌套求值(惰性求值)
akm_node secondary_call_exp;//二次调用
secondary_call_exp.m = node.m - ;
secondary_call_exp.n = -;//-1代表未知
secondary_call_exp.val = -;
exp_stack.push(secondary_call_exp);
akm_node tertiary_call_exp;//三次调用
tertiary_call_exp.m = node.m;
tertiary_call_exp.n = node.n - ;
tertiary_call_exp.val = -;
exp_stack.push(tertiary_call_exp);
continue;
}
}
return ;
} int main()
{
printf("============ 递归 ===========\n");
printf("akm(2,1)=%d\n", akm1(, ));
printf("\n");
printf("============ 非递归 ===========\n");
printf("akm(2,1)=%d\n", akm2(, ));
printf("\n");
return ;
}

递归算法(一)——akm的更多相关文章

  1. 金山云笔试题:AKM函数

    1. 题目描述 /** 阿克曼(Ackmann)函数 [题目描述] 阿克曼(Ackmann)函数A(m,n)中,m,n定义域是非负整数(m<=3,n<=10),函数值定义为: akm(m, ...

  2. 递归算法经典实例小结(C#实现)

     一 .递归算法简介 在数学与计算机科学中,递归是指在函数的定义中使用函数自身的方法. 递归算法是一种直接或者间接地调用自身算法的过程.在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往 ...

  3. 转: JAVA递归算法实例小结

    一.递归算法设计的基本思想是: 对于一个复杂的问题,把原问题分解为若干个相对简单类同的子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递推的出口,这样原问题就有递推得解. 在做递归算法的时候 ...

  4. xml 读取递归算法

    xml 读取递归算法:

  5. 后台返回字符串类型function的处理 (递归算法)

    $(function(){ $.ajax({ type: "post", url: "${ctx}/modules/fos/reference/echart", ...

  6. JAVA递归算法

    1.什么是递归算法 递归算法就是直接或间接调用自己的算法 2.问题1:一列数的规则如下: 1.1.2.3.5.8.13.21.34 ,求第30位数是多少?使用递归实现 public class Fib ...

  7. 【Tree 1】树形结构数据呈现的递归算法实现

    一.基本概况 在我的项目中,常常会用到树形结构的数据,最为明显的就是左边菜单栏,类似于window folder一样的东西. 而我之前一直是借助前端封装好的ZTree等工具实现展示,而后台则通常使用递 ...

  8. N个数全排列的非递归算法

    //N个数全排列的非递归算法 #include"stdio.h" void swap(int &a, int &b) { int temp; temp = a; a ...

  9. C# 递归算法与冒泡

    C# 递归算法求 1,1,2,3,5,8,13···static void Main(string[] args){ int[] cSum = new int[10];for (int i = 0; ...

随机推荐

  1. 格式化时间(SimpleDateFormat)

    import java.text.SimpleDateFormat; import java.util.Date; public class Main{ public static void main ...

  2. [SAP ABAP开发技术总结]面向对象OO

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  3. 小题精炼-----初试C语言

    毫无疑问, 这个又是给新入门学C语言的小鲜肉准备的! 题解: 1. 直接两个循环就ok啦! 当初讲课的时候几乎讲过原题啦. 2. 直接 100的三次方就是1000000啦, 所以直接对某数的三次方开方 ...

  4. 【转】移动端input输入placeholder垂直不居中

    在移动端编写input输入框时候,为了输入文字与输入框垂直居中,一般情况下,会将input的line-height的高度等于height.但在移动端输入的时候会发现,虽然输入内容确实是垂直居中了,但是 ...

  5. JS获取URL中参数值(QueryString)的4种方法分享<转>

    方法一:正则法 复制代码代码如下: function getQueryString(name) {    var reg = new RegExp('(^|&)' + name + '=([^ ...

  6. Android中view动画

    [1]透明 //点击按钮 实现iv 透明的效果 动画 public void click1(View v) { //1.0意味着着完全不透明 0.0意味着完全透明 AlphaAnimation aa ...

  7. Android批量图片加载经典系列——采用二级缓存、异步加载网络图片

    一.问题描述 Android应用中经常涉及从网络中加载大量图片,为提升加载速度和效率,减少网络流量都会采用二级缓存和异步加载机制,所谓二级缓存就是通过先从内存中获取.再从文件中获取,最后才会访问网络. ...

  8. Android ViewPager更新数据

    ViewPager也是一个常用的组件 与ListView类似 当绑定数据后 想刷新数据 需要在适配器中添加如下方法 protected PagerAdapter galleryAdapter = ne ...

  9. Hbase之进行批处理操作

    import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; impo ...

  10. python进阶

    决定在python上有所突破 先看看知乎: 如何面试Python后端工程师? Python之美 - 知乎专栏 Python书籍推荐