任意输入解决河内塔

时间:2015-05-27 20:43:50

标签: algorithm recursion

我想解决河内塔的任意(但有效)输入
例如,我有一个有3张光盘的游戏:

A    B    C

|    |    |
|    |    1
|    3    2

维基百科上给出的迭代和递归算法因此输入失败。递归停止在错误的位置或尝试非法移动而迭代的移动不会终止。

是否有“简单”的算法来解决这个问题? 我发现的唯一其他问题是Towers of Hanoi - giving help to the user mid game,而且这两个答案都没有帮助。

4 个答案:

答案 0 :(得分:1)

对于维基百科上的解决方案:它们只是用于将堆栈从一个引脚移动到另一个引脚,而不是用于任何输入。您可以考虑使用一些路径查找算法(适用于任何有效输入和预期输出)。

define state := a state of the hanoi tower (the positions of all elements on the pins).

define listMoves := returns a list of all valid moves that can made from the specified state

define solve:
    input: state start , state end
    output: void

    list visited
    list nextStates
    add(nextStates , start)

    while NOT isEmpty(nextStates)
        state s = remove(nextStates , 0)

        if s == end
             return

        add(visited , s)

        for state n : listMoves(s)
            if NOT contains(visited , n)
                add(nextStates , n)
                add(visited , n)

这只是一些简单的BFS。你也可以使用Dijkstra或A *。

答案 1 :(得分:0)

简单但效率不高的答案:

首先在较小的问题上运行算法以获得更大的标准,然后在更大的问题上运行算法 在给定的示例中,您将运行SolveHanoi(2, C, B)然后SolveHanoi(3, B, A)将所有三张光盘从给定状态移动到A.

更有效的解决方案:

请注意,递归解决方案(如果我的记忆服务正确)会针对较小的问题运行算法,移动一张光盘,然后针对相同的小问题运行算法,稍有不同。

我建议您尝试(或以某种方式检查),运行原始算法,但不是全部 - 只需要从您给定的状态运行的部分。

<强>澄清:

看一下这个例子,想一想:
如果有一个游戏,所有光盘都在轮询B上,你需要将它们移动到A,算法将:
1.移动光盘1&amp; 2至C.
2.将3移至A.
3.移动1&amp; 2到A.

您获得的输入是阶段#1之后的状态。

您的解决方案应该这样做:
1.实现什么是可能的初始状态(没有太多的选择,因为有三个民意调查开始) 2.在NOT-REAL-OP模式下运行递归算法(实际上不做任何动作),直到达到给定状态 3.继续以REAL-OP模式运行算法。

希望这有用。

答案 2 :(得分:0)

好吧,我说有一个&#34; easy&#34;算法:蛮力,递归搜索所有可能的移动,直到找到目标状态。那&#34;容易&#34;作为算法,但可能需要一些时间来处理,具体取决于您的问题的大小。此外,对于使用m个光盘的n个堆,可能存在可能的状态,因此更大的谜题可能会遇到内存问题。

例如,使用Dijkstra's algorithm

  • Dijkstra&#34; graph&#34;是所有可能的董事会状态的集合。
  • Dijkstra&#34;节点&#34;是单板状态。
  • &#34;距离&#34;从一个节点到下一个节点是移动的数量 需要到达那里。

然后你可以使用&#34; infinite graph&#34;优化以避免必须在初始化阶段枚举所有状态。

归结为实际问题,我认为主要问题是&#34;存储董事会状态的最佳方式是什么?&#34;。为了清晰起见,我在这里使用了略有不同的例子(3个桩,4个碟片):

A    B    C
|    |    |
|    3    1
|    4    2

对电路板进行建模的一种简单方法是为每个堆设置一个整数数组,每个数组包含该堆上的光盘数:

A -> []
B -> [3,4]
C -> [2,1]

然而,我的第一直觉是反过来,只存储一个包含每个光盘所在的堆的数组(1在C上,2在C上,3在B上,4在B上) ):

Discs = [C,C,B,B]       // four entries, one for each disc

这完全捕获单个数组中的板状态,并且易于比较(如果需要,您可以在此处存储单个字符串:&#34; CCBB&#34;)。

要计算下一个可能的移动,只需要遍历数组:第一次看到一个字母,那是该堆上最小的光盘(A上没有任何东西,3位于B的顶部,1位于C)的顶部:

Piles = [0,3,1]        // three entries - one for each pile

然后你可以将任何条目移动到空堆(0)或更大的数字,因此可能的移动是:

3 -> 0 : B -> A : Discs[3] = "A"  // move disc 3 from B to A giving [C,C,A,B]
1 -> 0 : C -> A : Discs[1] = "A"  // move disc 1 from C to A giving [A,C,B,B]
1 -> 3 : C -> B : Discs[1] = "B"  // move disc 1 from C to B giving [B,C,B,B]

我想如果我真的写了这篇文章,我可能最终会在每个堆上缓存最小的光盘以保存每次重新计算它,但这个想法是一样的。

祝你好运。

答案 3 :(得分:0)

河内迭代解决方案塔 import java.util.Arrays;

公共课TowerOfHanoi {

private static int SIZE = 5;

private class stack {

    stack(int size) {
        dataElements = new int[size];
    }
    int[] dataElements;

    int top = -1;

    private void push(int element) {
        dataElements[++top] = element;
    }

    private int pop() {
        if(top==-1) {
            return -1;
        }
        int topEle = dataElements[top];
        dataElements[top]=0;
        top --;
        return topEle;
    }

    private int top() {
        if(top==-1) {
            return -1;
        }
        return dataElements[top];
    }

    private boolean isEmpty() {
        return top == -1;
    }
}

public static void main(String[] args) {
    towerOfHanoi(SIZE);
}

private static void towerOfHanoi(int number) {
    initialize(number);
    if(number % 2 == 0) {
        solveEven(number);
    } else {
        solveOdd(number);
    }
}

private static int recentMoved = -1;

private static stack source = new TowerOfHanoi().new stack(SIZE);
private static stack intermediate = new TowerOfHanoi().new stack(SIZE);
private static stack destination = new TowerOfHanoi().new stack(SIZE);

private static void solveEven(int number) {

    while(destination.top < number-1) {
        if(!movePlates(source, intermediate, destination)) {
            if(!movePlates(intermediate,destination,source)) {
                if(!movePlates(destination, source, intermediate)){
                    continue;
                }
            }
        }
    }

}



private static boolean movePlates(stack from , stack dest1 ,stack dest2) {
    boolean movedPlate = false;
    if(from.top()== recentMoved) {
        return movedPlate;
    }
    if((!from.isEmpty()) && from.top()<(dest1.top()==-1?10000:dest1.top())) {
        dest1.push(from.pop());
        recentMoved=dest1.top();
        movedPlate = true;
    }
    else if((!from.isEmpty()) && from.top()<(dest2.top()==-1?10000:dest2.top())){
        dest2.push(from.pop());
        recentMoved=dest2.top();
        movedPlate = true;
    }
    if(movedPlate)
        display();
    return movedPlate;
}

private static void display() {
    Arrays.stream(source.dataElements).forEach(System.out::print);
    //.stream().fl.forEach(System.out::print);
    System.out.print("\t");
    Arrays.stream(intermediate.dataElements).forEach(System.out::print);
    System.out.print("\t");
    Arrays.stream(destination.dataElements).forEach(System.out::print);
    System.out.print("\n");
}


private static void initialize(int number) {
    for(int i=number;i>0;i--) {
        source.push(i);
    }
}

private static void solveOdd(int number) {
    while(destination.top < number-1) {
        if(!movePlates(source, destination, intermediate)) {
            if(!movePlates(destination,intermediate,source)) {
                if(!movePlates(intermediate, source, destination)){
                    continue;
                }
            }
        }
    }

}

}