递归地断言河内塔的移动数量

时间:2016-06-16 18:52:50

标签: java recursion assert assertion

我们在Uni给出的任务之一就是编写一个函数,以递归方式打印河内塔拼图的移动:

public static void move(int number, char start, char help, char end) {
    if (number == 1) {
        print("Move the top disk from " + start + " to " + end);
    } else {
        move(number - 1, start, end, help);
        print("Move the top disk from " + start + " to " + end);
        move(number - 1, help, start, end);
    }
}

现在我们必须提出一个函数来计算n元素的移动次数,并使用断言来使用此函数检查代码的有效性。

显然,该函数由f(n) = 2*f(n-1) + 1 n > 1f(n) = 1 n = 1给出。我们可以解决这个递归方程并获得f(n) = 2^n - 1

通过将static int count = 0;添加到类的顶部并在每个print语句后递增它,我们可以获得移动的总数:

public static void move(int number, char start, char help, char end) {
    if (number == 1) {
        print("Move the top disk from " + start + " to " + end);
        count++;
    } else {
        move(number - 1, start, end, help);
        print("Move the top disk from " + start + " to " + end);
        count++;
        move(number - 1, help, start, end);
    }
}

然后在函数调用之后添加一个断言,用递归方程的求解形式检查counter的值:

move(n, 'A', 'B', 'C');
assert count == Math.pow(2,n) - 1 : "Number of movements isn't correct.";

这很好用。但是,我很想知道是否有一种方法可以在递归函数本身内部使用assert,并使用递归形式的等式检查移动的数量 - 类似于assert count == 2*f(n-1) + 1。我们可能不得不改变count的用途,但我不知道如何(或者根本不可能)。

注意:print()仅代表标准System.out.println()

编辑:我更喜欢不需要更改move功能签名的解决方案(或者有人说如果没有这样的改变,这肯定是不可能的)< / em>的

2 个答案:

答案 0 :(得分:1)

一种方法是将计数作为参数添加到函数

public static int move(int number, char start, char help, char end, int count)

初始通话类似于

int count == Math.pow(2,n) - 1
move(n,'A','B','C',count);

然后在函数

public static int move(int number, char start, char help, char end,int count){
    if(number == 1){
        print("Move the top disk from " + start + " to " + end);
        assert count == 1; 
        return 1;
    }else{
        int subCount1 = move(number-1,start,end,help, (count-1)/2);
        print("Move the top disk from " + start + " to " + end);
        int subCount2 = move(number-1,help,start,end, (count-1)/2);
        assert count == (subCount1 + subCount2 + 1);
        return count; // it's the same as returning 2*f(n-1)+1;
    }
}

count参数用作预期的断言值。 这是纯粹的直觉,可能需要一些细微的改变。我不是(count-1)/2部分的100%。

修改 如果您不想更改方法签名,请尝试以下方法:

public static void move(int number, char start, char help, char end) {
    if (number == 1) {
        print("Move the top disk from " + start + " to " + end);
        count++;
    } else {
        int stepsBeforeMove1 = count;
        move(number - 1, start, end, help);
        int stepsAfterMove1 = count;
        print("Move the top disk from " + start + " to " + end);
        count++;
        int stepsBeforeMove2 = count; //this is just for the sake of clarity
        move(number - 1, help, start, end);
        int stepsAfterMove2 = count;
        assert ((stepsAfterMove1-stepsBeforeMove1) + (stepsAfterMove2-stepsBeforeMove1) + 1) == Math.pow(2,number) - 1;
    }
}

答案 1 :(得分:0)

如果您将游戏主板与解算器分开(因此Towers采用move方法,而Solver具有solve(towers)),则可以装饰{{1} } Towers。但是你必须摆脱静态方法,你会得到一些过度设计的OO代码而不是程序代码。