Ackermann函数(阿克曼函数)的递归、非递归(手动栈模拟)
目录一、Ackermann函数二、C++实现1. 递归实现合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入一、Ackermann函
一、Ackermann函数
A c k ( m , n ) = { n + 1 m = 0 A c k ( m − 1 , 1 ) m > 0 , n = 0 A c k ( m − 1 , A c k ( m , n − 1 ) ) m > 0 , n > 0 Ack(m,n)= \begin{cases} n+1& m=0\\ Ack(m-1,1)& m>0,n=0\\ Ack(m-1,Ack(m,n-1))& m>0,n>0 \end{cases} Ack(m,n)=⎩⎪⎨⎪⎧n+1Ack(m−1,1)Ack(m−1,Ack(m,n−1))m=0m>0,n=0m>0,n>0
Ackermannn函数输出值增长速度非常快,仅是对于(4,3)的输出已大得不能准确计算。
A c k ( 0 , n ) = n + 1 A c k ( 1 , n ) = n + 2 A c k ( 2 , n ) = 2 n + 3 A c k ( 3 , n ) = 2 n + 3 − 3 A c k ( 4 , n ) = 2 2 2 ⋅ ⋅ ⋅ 2 − 3 ( n + 3 个 2 ) Ack(0,n)=n+1\\ Ack(1,n)=n+2\\ Ack(2,n)=2n+3\\ Ack(3,n)=2^{n+3}-3\\ Ack(4,n)=2^{2^{2^{\cdot^{\cdot^{\cdot^{2}}}}}}-3(n+3个2) Ack(0,n)=n+1Ack(1,n)=n+2Ack(2,n)=2n+3Ack(3,n)=2n+3−3Ack(4,n)=222⋅⋅⋅2−3(n+3个2)
二、C++实现
1. 递归实现
按照表达式直接写,非常简单。
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));
}
2. 栈模拟递归
观察表达式,显然只有 m = 0 m=0 m=0的情况才能直接求得具体值,因此递归的出口一定 m = 0 m=0 m=0。
先将问题具体化,例如求 A c k ( 2 , 1 ) Ack(2,1) Ack(2,1)的值,手动演算一下,每一次取栈顶的时候都同时出栈,栈的变化如图:
先分析不那么特殊的两种情况:
① n = 0 n=0 n=0,如蓝框过程。
则 ( m − 1 , 1 ) (m-1,1) (m−1,1)入栈。
② m > 0 , n > 0 m>0,n>0 m>0,n>0,如红框过程。
由于 n n n的值也需要通过 A c k Ack Ack函数求出,先标记为 − 1 -1 −1,即待求值,将 ( m − 1 , − 1 ) (m-1,-1) (m−1,−1)入栈;
然后将求 n n n的值的 A c k Ack Ack函数入栈,即 ( m , n − 1 ) (m,n-1) (m,n−1)入栈。
然后考虑 m = 0 m=0 m=0的情况:
如绿框过程。由于 m = 0 m=0 m=0时,函数求得了具体值,先更新返回值ret为n + 1。
通过栈是否为空判断是中间过程还是递归出口,栈空则说明达到了递归出口。
①中间过程,即前面一个绿框过程。
返回值为之前讨论的“用-1标记的待求的n”,因此更新栈顶的n。
②递归出口,即后面的绿框过程。栈空,说明没有待更新、待求值的了,该返回值即最终结果。
#include <stack>
int Ack_non_recursion(int m, int n) {
struct Ack { // Ackermann结构体
int m_, n_;
Ack(int m, int n) : m_(m), n_(n) {} // 构造函数
} Node(m, n);
stack<Ack> s; // STL栈
s.push(Node); // 初始值入栈
int ret = -1; // Ackermann函数的返回值
while(!s.empty()) {
Node = s.top(); // 取栈顶的值
s.pop(); // 并且出栈
if(Node.m_ == 0) { // 第一种情况,m 等于 0
ret = Node.n_ + 1; // 返回值为 n + 1
if(s.empty()) // 如果栈空了,说明不需要继续向上返回,已经得到了最终结果
break; // 跳出循环
Node = s.top(); // 否则,更新栈顶结点(第三种情况中 用 -1 标记的待计算的 n 的值)
s.pop(); // 先出栈
Node.n_ = ret; // n 修改为返回值
s.push(Node); // 再重新入栈
}
else if(Node.n_ == 0) { // 第二种情况,n 等于 0
s.push(Ack(Node.m_ - 1, 1)); // Ack(m - 1, 1) 入栈
}
else { // 第三种情况,m, n 都大于 0
s.push(Ack(Node.m_ - 1, -1)); // 用 -1 标记 n ,表示还没有计算出值,Ack(m - 1, -1) 入栈
s.push(Ack(Node.m_, Node.n_ - 1)); // 要计算 n 的值,Ack(m, n - 1) 入栈
}
}
return ret;
}
更多推荐



所有评论(0)