返回狼盟编程首页
编程搜索 [狼盟旧档]
论坛统计


请输入搜索关键字:
├─◆ 狼盟首页 > 查看贴子 > 详细信息

楼主

发了N次都没人能解决????????????


求解阿克曼函数问题.为什么当m=4,n=1或m=3,n=11时就不行,就会溢出,这个问题怎么解决呀,请各位指点.定义阿克曼递归函数:ACK(0,n)=n+1         n>=0ACK(m,0)=ACK(m-1,1)        m>=1ACK(m,n)=ACK(m-1,ACK(m,n-1))      m,n>0





hanshuyujifen [ 1 楼 ]
2006-06-01 02:20:00
这个函数是干什么用的?定义不难理解呀!!我拿笔画了一下,没什么问题!!道低是什么溢出? 

joky [ 2 楼 ]
2006-06-01 12:09:00
你输入m=4,n=1或m=3,n=11看看就知道了,它不会输出这个函数最后的计算结果,更不用说是输出调用了这个函数的次数了.溢出就是你的数据超出了VC默认的stack size(1M),而当m=4,n=1或m=3,n=11时的计算结果却超出这个大小, 

scyangbo [ 3 楼 ]
2006-06-01 13:07:00
记忆化搜索不就行了? 

nyra [ 4 楼 ]
2006-06-01 13:17:00
这个东西不能这么算,递归的太快了.需要用非递归的算法.作业? 

scyangbo [ 5 楼 ]
2006-06-01 13:25:00
不一定要用非递归啊,记忆化搜索又容易理解,速度又快…… 

scyangbo [ 6 楼 ]
2006-06-01 13:50:00
你是不是还是不懂?我发个程序算了……#include<stdio.h>#define max 100long nums[max+1][max+1],m,n,i,j;void init(void){   for(i=0;i<=max;++i)      for(j=0;j<=max;++j)      nums[i][j]=0;}long cal(long m,long n){   if (nums[m][n]!=0)return nums[m][n];   else   {      nums[m][n]=(m==0)?n+1:( (n==0)?cal(m-1,1):cal(m-1,cal(m,n-1)) );      return nums[m][n];   }}main(){   scanf("%ld%ld",&m,&n);   init();   printf("%ld ",cal(m,n));   getch();} 

acolyte [ 7 楼 ]
2006-06-01 14:06:00
//-----------------------------------------#include<iostream>int ACK(int lh, int rh){    if (0 == lh)     {        return rh + 1;    }    if (lh >= 1 && 0 == rh)    {        return ACK(lh - 1, 1);    }    if (lh > 0 && rh > 0)    {        return ACK(lh - 1, ACK(lh, rh - 1));    }}void main(){    int result = ACK(4, 1);    std::cout<<"result= "<<result<<std::endl;}这个函数有时候是无限循环,不可能有结果的,你自己debug一下这个程序就知道了:) 

joky [ 8 楼 ]
2006-06-01 15:51:00
记忆化搜索?能说说是怎么样的吗?你那个程序我刚刚试过了,得到的结果不正确,即使是很小的数也不正确, 

joky [ 9 楼 ]
2006-06-01 15:52:00
我写的程序也跟你的一样,不过得不到任何结果.你用的是STUDIO.NET?还是? 

joky [ 10 楼 ]
2006-06-01 15:54:00
不好意思,让各位久等了,我刚下课.我刚刚试过了你们的程序,我是用递归写的,不过我也试了你们的程序,都不行,得不到结果. 

acolyte [ 11 楼 ]
2006-06-01 16:05:00
你确定一定可以得到结果吗?结果是多少你知道吗? 

acolyte [ 12 楼 ]
2006-06-01 16:06:00
你确定任何两个数的组合都能得到解吗? 

joky [ 13 楼 ]
2006-06-01 16:10:00
至少我输入m<=3,n<=10时任何两个组合能得到结果.超过就不能得到结果.难道就没有解决的办法吗? 

acolyte [ 14 楼 ]
2006-06-01 16:20:00
你用那个(4,1)的组合试一下,会发现调用到一定步骤后就会出现循环,因此是无解的 

joky [ 15 楼 ]
2006-06-01 16:36:00
这个递归本来就是调用很多次的,如果要调试看它能否到哪里开始循环,真不知道要调到什么时候. 

acolyte [ 16 楼 ]
2006-06-01 17:07:00
我保证不超过100次:)。而且我一直想知道这个问题是否对任意两个数都有解,如果存在无解的情况,就没必要讨论了。程序的栈是有限的,如果调用次数循环下去,当然不会有结果。 

overfly [ 17 楼 ]
2006-06-01 17:11:00
#include<stdio.h>#include<stdlib.h>#include<time.h>int ack(int,int);long number;int main(){  int m,n;  long t;  if(scanf("%d%d",&m,&n)==2)  {    t=time(NULL);     printf("the result is %d ",ack(m,n));    printf("the time is %ld ",t=time(NULL)-t);    printf("the number is %ld ",number);  }  system("pause");  return 0;}int ack(int m,int n){  number++;  if(m==0&&n>=0)    return n+1;  if(m>=1&&n==0)    return ack(m-1,1);  if(m>0&&n>0)    return ack(m-1,ack(m,n-1));}输入4 1结果65533,时间103 函数运行溢出次数(导致时间延时1)输入3 11结果16381,时间5 函数运行178875096 

joky [ 18 楼 ]
2006-06-01 17:25:00
VC6.0中默认的stack size是1M,如果将它改大的话,输入(4,1)这个组合就能得到结果,目的是现在不想通过改它默认的大小就能计算出结果. 

joky [ 19 楼 ]
2006-06-01 17:27:00
强.结果是正确的.不过我是通过改VC6.0中默认的stack size的大小来看到结果的,不知如何不通过改它的大小来计算出结果.对了,你这个程序能输入的M或N有多大?能不能输入负数?我定义了一个count来输出它调用的次数,类型是long int ,不过输出是的随机数.是不是类型不对? 

sarrow [ 20 楼 ]
2006-06-01 17:36:00
在19楼的基础上做了些修改:#include<stdio.h>#include<stdlib.h>#include<time.h>int ack(int,int);const int len = 20;static table[len][len*len*len*len];int main() {    int m, n;    long t;    if (scanf("%d%d", &m, &n) == 2) {        t = time(NULL);         printf("the result is %d ",ack(m,n));        printf("the time is %ld ",t = time(NULL)-t);    }}int ack(int m, int n) {    if (m < 0 || m >= len) {        puts("m wrong");        system("pause");    }    if (n < 0 || n >= len * len * len * len) {        puts("n wrong");        printf("n = %d ", n);        system("pause");    }    if (table[m][n]) {        return table[m][n];    }    if (m == 0 && n >= 0) {        return table[m][n] = n + 1;    }    if (m >= 1 && n == 0) {        return table[m][n] = ack(m - 1, 1);    }    if (m > 0 && n > 0) {        return table[m][n] = ack(m-1, ack(m, n-1));    }}/*输入4 1结果65533,时间103输入3 11结果16381,时间5*/ 

joky [ 21 楼 ]
2006-06-01 18:07:00
高手.不过我调试了你写的程序,输入(3,11)时的结果是正确的,计算的时间是0,输入(4,1)时的结果是press any key to continue,没有任何结果. 

joky [ 22 楼 ]
2006-06-01 18:10:00
能否解释下if(scanf("%d%d",&m,&n)==2)这句的意思?下面是我写的程序,好像没什么差别,为什么我的程序在调试时输入(3,11)或(4,1)时就得不到结果呢?#include<iostream.h>long int count=0;long int ACK(int m, int n) {     long int temp;     count++;    if (m == 0)          return (n + 1);     else if (n == 0)          return ACK(m - 1, 1);     else    {         temp = ACK(m, n - 1);         return ACK(m - 1, temp);     } }int main(int argc, char* argv[]){    int m,n;    cin>>m>>n;    cout<<ACK(m,n)<<endl;    cout<<"共调用 "<<count<<" 次!"<<endl;    return 0;} 

overfly [ 23 楼 ]
2006-06-01 18:10:00
sarrow兄没帮我加上return 0;只有自己修改一下 不过我是通过改VC6.0中默认的stack size的大小来看到结果的,不知如何不通过改它的大小来计算出结果.——————————————————————————————————如果不改变栈区大小的话,似乎只有寻找更为简单的算法了。。。我用的不是vc6.0,所以没遇到这种情况对了,你这个程序能输入的M或N有多大?能不能输入负数?——————————————————————————————————我没试过,最好不要太打。不然不知道要等多长时间,可以输入负数,那么函数什么也不执行,至于返回什么也是未知的。所以在函数体里面加个判断:if(m<0||n<0)return 0;(假设m或n小于0时结果为0)我定义了一个count来输出它调用的次数,类型是long int ,不过输出是的随机数.是不是类型不对?——————————————————————————————————当输入4 1时函数调用次数已经超过了long int的最大值,他会从long int的取值范围内从正跳到负,从负跳到正,直到long int能够表示他为止。所以从某中意义上讲如果知道了跳转的次数,就可以根据这个“随机”数得出函数真正调用的次数 

overfly [ 24 楼 ]
2006-06-01 18:18:00
scanf函数返回成功读取数据的个数 

joky [ 25 楼 ]
2006-06-01 18:21:00
难道就只有通过改变栈的大小来实现吗?说真的我还真想不到其它的算法.你用的是什么来编译的? 

sarrow [ 26 楼 ]
2006-06-01 18:23:00
4 1the result is 65533the time is 0请按任意键继续. . .在BCB55下结果 

overfly [ 27 楼 ]
2006-06-01 18:25:00
devcpp-4.9.9.2 

joky [ 28 楼 ]
2006-06-01 18:31:00
是不是不同的编译器会产生不同的结果?好像这也是我们编程员不想看到的吧. 

joky [ 29 楼 ]
2006-06-01 18:37:00
你有没有改栈的大小?在我这边如果没改栈的大小的话是看不到结果的. 

sarrow [ 30 楼 ]
2006-06-01 18:48:00
你用的是VC6么?扔了吧!(换成BCB系列、GCC系列或用VC的高级版本VC7、VC8) 

zhgc1983 [ 31 楼 ]
2006-06-01 18:55:00
算法有误,你的算法有死循环!顺便问下阿克曼函数的定义是什么“? 

joky [ 32 楼 ]
2006-06-01 18:55:00
是呀,我用的是VC6.0,换成你说的那些版本能解决这个函数的溢出问题? 

joky [ 33 楼 ]
2006-06-01 19:00:00
定义阿克曼递归函数:ACK(0,n)=n+1         n>=0ACK(m,0)=ACK(m-1,1)        m>=1ACK(m,n)=ACK(m-1,ACK(m,n-1))      m,n>0哪里有死循环? 

sarrow [ 34 楼 ]
2006-06-01 19:08:00
是呀,我用的是VC6.0,换成你说的那些版本能解决这个函数的溢出问题?不能!ACM函数的增长非常快!除非你把数据类型换成double--要不然,简单的数值都没有办法计算!换成GCC、BCB等,而仍然用int或long,只能缓解溢出的情况而已! 

joky [ 35 楼 ]
2006-06-01 19:18:00
好,试试看,谢谢各位的指点.这里真是高手如云. 

sarrow [ 36 楼 ]
2006-06-01 19:24:00
关键是找到不用递归的方法,这才能抵消递归栈对内存的开销! 

joky [ 37 楼 ]
2006-06-01 19:50:00
试过,不过都不理想,没能实现. 

天国龙 [ 38 楼 ]
2006-06-01 21:55:00
引用:你是不是还是不懂?我发个程序算了……#include<stdio.h>#define&nbsp;max&nbsp;100long&nbsp;nums[max+1][max+1],m,n,i,j;void&nbsp;init(void){&nbsp;&nbsp;&nbsp;for(i=0;i<=max;++i)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(j=0;j<=max;++j)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nums[i][j]=0;}long&nbsp;cal(long&nbsp;m,long&nbsp;n){&nbsp;&nbsp;&nbsp;if&nbsp;(nums[m][n]!=0)return&nbsp;nums[m][n];&nbsp;&nbsp;&nbsp;else&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nums[m][n]=(m==0)?n+1:(&nbsp;(n==0)?cal(m-1,1):cal(m-1,cal(m,n-1))&nbsp;);//(*)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nums[m][n];&nbsp;&nbsp;&nbsp;}}main(){&nbsp;&nbsp;&nbsp;scanf("%ld%ld",&m,&n);&nbsp;&nbsp;&nbsp;init();&nbsp;&nbsp;&nbsp;printf("%ld ",cal(m,n));&nbsp;&nbsp;&nbsp;getch();}问题出在(*)哪行:cal(m-1,cal(m,n-1)),这里即使初始的m,n很小cal(m,n-1)也有可能会很大以至超过max,的出的结果不正确.例如m=3 n=7按照流程nums[3][7]=cal(2,cal(3,6))=nums[2][cal(3,6)]而用一般的算法cal(3,6)=509也就是nums[3][7]=nums[2][509]由于没有nums[2][509]所以返回的数值会有问题.明显的是对任意m,n都有nums[m][n]=nums[m-1][x]由于随着m的增加nums[m][n]=nums[m-1][x]中x会增加得异常快,在下实在想不到一个好的算法. 

joky [ 39 楼 ]
2006-06-01 22:19:00
是的,就是因为如此,所以才会.........不过还是谢谢了,大家出来想想交流交流 

zhgc1983 [ 40 楼 ]
2006-06-02 09:05:00
不好意思,看错了!没有死循环!谢罪了! 

vire [ 41 楼 ]
2006-06-02 15:02:00
简单递归在m=3,n=11的时候运行一段时间就可以出结果.而m=4,n=1的时候不行.通项:    A(0, n) = n + 1     A(1, n) = 2 + (n + 3) - 3     A(2, n) = 2 × (n + 3) - 3     A(3, n) = 2 ^ (n + 3) - 3 m=4的通项:    A(4,n) = 2^2^...^2 - 3      (n+3个2)    A(4,1) = 2^2^2^2 - 3 = 65533    A(4,2) = 2^65536-3    Ackermann's function 增长太快.    并且在m=3时候递归的次数已经相当的大.在m>3的时候,简单的递归程序已经不能解决这个问题了.参考:    http://www-users.cs.york.ac.uk/susan/cyc/a/ackermnn.htm 

zhgc1983 [ 42 楼 ]
2006-06-02 17:09:00
#include "testAckm.h"int main(int argc, char **argv){    __int64 nResult;    int m;    __int64 n;    if (argc != 3) {        printf("Error input: ");        return -1;    }    m = atoi(argv[1]);    n = atoi(argv[2]);    if (m < 0 || n < 0) {        printf("Error param: ");        return -1;    }    nResult = ACK(m, n);    printf("ACK(%d, %I64u) = %I64u ", m, n, nResult);    return 0;}__int64 ACK(int m, __int64 n){    __int64 nRet;    if (0 == m && n >= 0) {        nRet = n + 1;        goto FINISH;    }    if (m == 1 && n >= 0) {        nRet = n + 2;        goto FINISH;    }    if (m == 2 && n >= 0) {        nRet = (2 * n) + 3;        goto FINISH;    }    if (m == 3 && n >= 0) {        nRet = (__int64)POW(2, n + 3) - 3;        goto FINISH;    }    if (m >= 4 && 0 == n) {        nRet = ACK(m - 1, 1);        goto FINISH;    }    if (m > 0 && n > 0) {        nRet = ACK(m - 1, ACK(m, n - 1));        goto FINISH;    }FINISH:    return nRet;}__int64 POW(__int64 a, __int64 b){    __int64 nRet = 1;    if (b < 0)        nRet = 0;    for (; b > 0; b--) {        nRet *= a;    }    return nRet;}运行下,太恐怖了! 

joky [ 43 楼 ]
2006-06-02 20:21:00
是呀,不过我听说有人用堆栈做到了可以算到m=5,n=7.真是强,不知他是怎么做的?又找不到人. 

joky [ 44 楼 ]
2006-06-02 20:24:00
强,不过那个testAckm.h都没有,怎么运行呀?发过来我调试下看看有什么效果?谢了. 

zhgc1983 [ 45 楼 ]
2006-06-05 10:59:00
晕了吧#ifndef _testAckm_h#define _testAckm_h#include <stdio.h>#include <stdlib.h>#include <windows.h>__int64 ACK(int m, __int64 n);__int64 POW(__int64 a, __int64 b);#endif 

joky [ 46 楼 ]
2006-06-05 11:10:00
谢了,不过你是不是写错呀,编译时还是提示说:fatal error C1083: Cannot open include file: 'testAckm.h': No such file or directory. 

zhgc1983 [ 47 楼 ]
2006-06-05 13:04:00
晕哦,我贴上来的,拷贝到'testAckm.h'文件里面就可以了!