C# 阿克曼终止:根本原因分析

C# 阿克曼终止:根本原因分析,c#,c++,terminate,termination,ackermann,C#,C++,Terminate,Termination,Ackermann,可能不需要太多的解释来解释这是什么,它甚至完全按照我想要的方式工作。我真正的问题是程序终止。我已经跟踪了我的返回值和我在主函数中使用的嵌套for循环,以逐步遍历这些值。我看不出发生这种情况的原因,但在最后一次通过我的循环后,程序需要额外10分钟才能真正终止。尽管我的循环索引正在增加(因为预检查),但我的Ackermann函数显然没有执行额外的迭代(无论如何我也不希望这样)。另一方面,唯一合乎逻辑的解释是循环没有中断,但如果是这样,我的Ackermann函数应该返回一个新的b值。所以我能想到的另一

可能不需要太多的解释来解释这是什么,它甚至完全按照我想要的方式工作。我真正的问题是程序终止。我已经跟踪了我的返回值和我在主函数中使用的嵌套for循环,以逐步遍历这些值。我看不出发生这种情况的原因,但在最后一次通过我的循环后,程序需要额外10分钟才能真正终止。尽管我的循环索引正在增加(因为预检查),但我的Ackermann函数显然没有执行额外的迭代(无论如何我也不希望这样)。另一方面,唯一合乎逻辑的解释是循环没有中断,但如果是这样,我的Ackermann函数应该返回一个新的b值。所以我能想到的另一个原因是垃圾收集花费了那么长时间来清除我的数据结构并刷新内存堆。对于那些不熟悉的人来说,这里的想法是实现传统上作为迭代函数表示的非常麻烦的递归函数。递归地:

给定正整数m和n: 如果m=0,则返回n+1;否则,如果n=0,则返回Ackermann(m-1,1);否则返回阿克曼(m-1,阿克曼(m,n-1))。因此,迭代的思想是使用堆栈来模拟递归函数调用,这样就可以使用堆中的内存,而不依赖于调用堆栈的大小,从而限制了执行时间。我担心我忽略了我的流程中的某些东西,这些东西导致了计算完成和程序到达用户完全退出的时间之间的长时间延迟

这是我的密码:

 static void Main(string[] args)
    {
        ulong i, j;
        i = j = 0;
        for (i = 1; i <= 3; i++)
            for (j = 1; j <= 15; j++)
                Console.WriteLine("[{0}] Ackermann({1},{2}) = {3}", DateTime.Now, i, j, Ackermann(i, j));
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

    static ulong Ackermann(ulong a, ulong b)
    {
        Stack<ulong> ackStack = new Stack<ulong>();

        ackStack.Push(a);
        while (ackStack.Count > 0)
        {
            a = ackStack.Pop();
            if (a == 0)
                b++;
            else if (b == 0)
            {
                ackStack.Push(--a);
                b = 1;
            }
            else
            {
                ackStack.Push(--a);
                ackStack.Push(++a);
                b--;
            }
        }
        return b;
    }
static void Main(字符串[]args)
{
尤龙一世,j,;
i=j=0;

对于(i=1;i非常感谢Pieter Witvoet!事实证明,我的Writeline中的求值顺序是错误的。只有在更改了它之后,才可以明显看出在优化之前(可能只是您正在经历的堆清理)增加了整个运行时的时间!

,确保您的代码是正确的。我没有分析它,但它看起来是错误的。我不怀疑代码可能有问题。我只是想知道是否有其他人会接受我关于堆清理的假设。我将逐步执行推送和弹出操作,并报告我发现的内容。在您的
控制台中。WriteLine
行中,
日期时间现在,
参数是在
Ackermann(i,j)
调用之前计算的,因此您记录的是每个Ackermann调用的开始时间,而不是调用完成的时间。因此,这10分钟是最后一个Ackermann调用所用的时间。垃圾收集和内存释放可能只需要毫秒(分配的对象不多,只是几个大缓冲区).Ohh,我甚至没有考虑求值顺序。这就是为什么有时你只需要另一双眼睛!那么关于WriteLine的问题。所有东西都是按照格式字符串的顺序求值的,还是按参数位置求值的?根据C语言规范,函数参数总是从左到右求值。这种情况以前发生过函数实际上已被调用,因此格式字符串不能影响这一点(它只是另一个参数本身)。最终未使用的参数仍必须进行求值。同样,多次使用的参数只会求值一次。