【经典算法实现 14】阿克曼函数---手动推导求解、递归实现、非递归实现
该题目来自腾讯实习生的招聘笔试,题目给出如下的递归函数,求ack(3,3)的值。
long ack(int m,int n)
{
if(m == 0){
return n+1;
}else if(n == 0){
return ack(m - 1, 1);
}else{
return ack(m - 1, ack(m, n - 1));
}
}
题目来自:《【腾讯笔试】已知递归函数计算ack(3,3)的值》
一、阿克曼函数 手动求解
ack(0, n) = n+1
ack(m, 0) = ack(m-1, 1)
ack(m, n) = ack(m-1, ack(m, n-1))
由此可见,m 每一行,都是一个等差数列
推导公式
=======> 当 m = 1 时
ack(1, 0) = ack(0, 1) = 1 + 1 = 2
ack(1, n) = ack(1, n-1 ) +1 = 2 + N
=======> 当 m = 2 时
ack(2, 0) = ack(1, 1) = 2+1 = 3
ack(2, n) = ack(1, ack(2,n-1)) = 2 + ack(2, n-1) = 2 * N + ack(2, 0) = 2*N + 3
可以得到,m = 2时,是等差为2 的等差数列
=======> 当 m = 3 时
ack(3, 0) = ack(2, 1) = 2 * 1 + 3 = 5
ack(3, n) = ack(2, ack(3, n-1)) = 2 * (ack(3, n-1)) + 3
计算如下:
ack(3, 1) = 2 * (ack(3,0)) + 3 = 2 * 5 + 3 = 13
ack(3, 2) = 2 * (ack(3,1)) + 3 = 2 * 13 + 3 = 29
ack(3, 3) = 2 * 29 + 3 = 61
ack(3, 4) = 2 * 61 + 3 = 125
ack(3, 5) = 2 * 125 + 3 = 253
ack(3, 6) = 2 * 253 + 3 = 509
ack(3, 7) = 2 * 509 + 3 = 1021
ack(3, 8) = 2 * 1021 + 3 = 2045
ack(3, 9) = 2 * 1021 + 3 = 2045
ack(3, 10) = 2 * 2045 + 3 = 4093
ack(3, 11) = 2 * 4093 + 3 = 8189
ack(3, 12) = 2 * 8189 + 3 = 16381
ack(3, 13) = 2 * 16381 + 3 = 32765
=======> 当 m = 4 时
ack(4, 0) = ack(3, 1) = 13
ack(4, n) = ack(3, ack(4, n-1)) = 2 * (ack(4, n-1)) + 3
ack(4, 1) = ack(3, ack(4, 0)) = ack(3, ack(3,1)) = ack(3, 13) = 32765
ack(4, 2) = ack(3, ack(4, 1)) = ack(3, 32765)
可以看到,当计算到 ack(4, 2)
时,整个数一定特别大了,人工去算基本是不可能的了。
二、阿克曼函数 递归实现
#include <stdio.h>
long long ack(int m, int n)
{
if(m == 0){
return n+1;
}else if(n == 0){
return ack(m - 1, 1);
}else{
printf("m = %d, n = %d\n", m, n);
return ack(m - 1, ack(m, n - 1));
}
}
int main(void)
{
int m=0, n=0;
long long val=0;
while(1){
scanf("%d %d", &m, &n);
val = ack(m, n);
printf("\nack(%d, %d) = %lld\n",m ,n, val);
}
}
实际使用计算机计算,由于是递归,做了很多重复的操作,整个求解过程也非常慢,
实测计算 m = 3, n = 6
,我的电脑用了差不多 6 s
更别说,m = 4 的时候了。
三、阿克曼函数 非递归实现
网上有个兄弟使用java stack 栈来实现的,有兴趣的兄弟可以看下:
《每天刷个算法题20160524:阿克曼函数的递归转非递归解法》
《关于阿克曼函数(akermann)非递归算法的一点见解 c++》
有关C语言的解法,待更新。。。先吃饭