【问题标题】:How to write Ackermann Function iteratively?如何迭代地编写阿克曼函数?
【发布时间】:2020-03-05 06:12:02
【问题描述】:

我写了一个递归版本的阿克曼函数,它工作正常:

int ackermann_r(int m, int n) {
    if(m == 0) {
        return n + 1;
    } else if(n == 0) {
        return ackermann_r(m - 1, 1);
    } else {
        return ackermann_r(m - 1, ackermann_r(m, n - 1));
    }
}

然后我尝试迭代地重写代码:

(我不知道怎么用malloc来使用二维数组,所以你会觉得代码很脏……)

int ackermann_i(int m, int n) {
    int* A = (int*) malloc((m+1) * (n+1) * sizeof(int));
    for(int i = 0; i <= m; i++) {
        for(int j = 0; j <= n; j++) {
            if(i == 0) {
                A[i*(n+1) + j] = j + 1;
            } else if(j == 0) {
                A[i*(n+1) + j] = A[(i-1)*(n+1) + 1];
            } else {
                A[i*(n+1) + j] = A[(i-1)*(n+1) + A[i*(n+1) + (j-1)]];
            }
        }
    }
    return A[m*(n+1) + n];
}

但迭代版本打印了错误的答案。例如:

m: 3
n: 2
recursive: 29
iterative: 3

为什么我的迭代代码不起作用?

【问题讨论】:

    标签: c algorithm ackermann


    【解决方案1】:

    未定义的行为

    很遗憾,由于对未初始化值的访问和越界访问,您的代码会显示未定义的行为。显示此行为的最简单测试是m = 1, n = 0。这表示外循环只有两次迭代,内循环只有一次迭代,因此更容易分析:

    int ackermann_i(int m, int n) {
        int* A = (int*) malloc((m+1) * (n+1) * sizeof(int));
        for(int i = 0; i <= m; i++) {
            for(int j = 0; j <= n; j++) {
                if(i == 0) {
                    A[i*(n+1) + j] = j + 1;              //       (1)
                } else if(j == 0) {
                    A[i*(n+1) + j] = A[(i-1)*(n+1) + 1]; //       (2)
                } else {
                    A[i*(n+1) + j] = A[(i-1)*(n+1) + A[i*(n+1) + (j-1)]]; // (3)
                }
            }
        }
        return A[m*(n+1) + n];
    }
    

    所以让我们手动迭代:

    • i = 0, j = 0。我们输入(1)并设置A[0 + 0] = 1
    • i = 1, j = 0。我们输入(2)并设置A[2 + 0] = A[0 + 1]
    • 总是至少有j == 0,所以我们不关心(3)

    但问题是:我们从未设置过A[0 + 1]。该值可能为零,也可能是随机的其他值;未定义的行为随之而来。更糟糕的是,我们的A不够大:(m+1)*(n+1)在这里只有2,所以A[2]是一个越界数组访问。

    这表明两个问题:

    • 我们分配的内存不够大,而且可能永远不会,因为a(m, a(m-1,n)) 中的内项可能会比n 大得多。
    • 如果我们有解决方案,我们需要先处理琐碎的情况,例如

      for(int j = 0; j <= (n+1); ++j) {
          A[0 + j] = j + 1;          // set all A[i,j] where i = 0
      }
      

    算法的一个更深层次的问题

    然而,还有一个更深层次的问题。您的代码暗示阿克曼函数可以在θ(m * n) 中计算。 That's however impossible。相反,您至少需要一个堆栈或类似的可以增大大小的东西来计算结果。 This implementation in Java 提供了一些灵感。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-28
      • 2012-01-31
      • 1970-01-01
      • 2015-07-13
      • 1970-01-01
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多