Algorithm 求解任意输入的河内塔

Algorithm 求解任意输入的河内塔,algorithm,recursion,Algorithm,Recursion,我想解决河内塔的任意但有效的输入。 例如,我有一个有3张光盘的游戏: A B C | | | | | 1 | 3 2 wikipedia上给出的迭代算法和递归算法都会因为这个输入而失败。递归在错误的位置停止或尝试非法移动,而迭代不会终止 有没有一个简单的算法来解决这个问题? 我发现的另一个问题是,两个答案都没有帮助。简单但不是很有效的答案: 首先在较小的问题上运行算法,得到标准的较大问题,然后在较大的问题上运行算法。 在给定的示例中,您将运行S

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

A    B    C

|    |    |
|    |    1
|    3    2
wikipedia上给出的迭代算法和递归算法都会因为这个输入而失败。递归在错误的位置停止或尝试非法移动,而迭代不会终止

有没有一个简单的算法来解决这个问题? 我发现的另一个问题是,两个答案都没有帮助。

简单但不是很有效的答案:

首先在较小的问题上运行算法,得到标准的较大问题,然后在较大的问题上运行算法。 在给定的示例中,您将运行SolveHanoi2、C、B,然后运行SolveHanoi3、B、A,以将所有三张光盘从给定状态移动到A

更有效的解决方案:

请注意,如果我的内存正常,递归解决方案会在一个较小的问题上运行算法,移动一张光盘,然后在同一个小问题上运行算法,只是略有变化

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

澄清:

查看给定的示例,请思考以下内容: 如果有一个游戏,其中所有光盘都在轮询B上,并且您需要将它们移动到a,则算法将: 1.将盘1和2移动到C。 2.移动3到A。 3.将1和2移动到A

您得到的输入是阶段1之后的状态

您的解决方案应该做到这一点: 1.了解什么是可能的初始状态不要有太多的选择,因为有三个投票开始。 2.在NOT-REAL-OP模式下运行递归算法,在达到给定状态之前不进行任何移动。 3.继续以实时操作模式运行算法


希望这会有帮助。

对于维基百科上的解决方案:它们只用于将堆栈从一个管脚移动到另一个管脚,而不用于任何输入。您可以考虑使用一些路径查找算法对任何有效输入和预期输出进行工作。
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或*。这是一个简单的算法,但可能需要一些时间来处理,这取决于你的问题的大小。此外,对于使用m个光盘的n堆,可能存在n^m个状态,因此较大的谜题可能会遇到内存问题

例如,使用:

Dijkstra图是所有可能的电路板状态的集合。 Dijkstra的节点是单板状态。 从一个节点到下一个节点的距离是移动的次数 需要去那里。 然后可以使用优化来避免在初始化阶段枚举所有状态

说到实用性,在我看来,主要的问题是存储董事会状态的最佳方式是什么?。为了清晰起见,我在这里使用了一个稍微不同的示例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
这完全可以在单个阵列中捕获电路板状态,并且易于比较。如果需要,您可以在此处存储单个字符串:CCBB

要计算下一步可能的移动,只需在阵列中运行:第一次看到字母时,即该堆中最小的圆盘上没有任何东西在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]
我想如果我真的写了这篇文章,我可能会在board状态下缓存每一堆中最小的磁盘,以避免每次重新计算,但想法是一样的


祝你好运。

河内塔迭代解 导入java.util.array

河内公共级塔楼{

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;
                }
            }
        }
    }

}

}

如果您指定所使用的语言并包含失败的代码,您可能会得到更多帮助。很抱歉,您的评论没有帮助,因为这个问题与语言无关。我的实现是正确的,但算法并不能解决我的问题。也许这会有帮助:对不起,这是一个付费墙的后面。我已经想到了这一点。问题是,递归算法永远不会达到这种状态,因为示例中的状态不在解决方案的快速集路径上。对于B中需要移动到A的3张光盘的问题,它位于最快路径上。@Seb:另外,请使用简单的,低效的回答。你能详细说明一些路径查找算法吗?否则,你的答案并没有真正的帮助。