有关使用15平方拼图的A *的问题

时间:2009-12-23 19:31:10

标签: algorithm graph a-star

我正在尝试为A* solver构建15-square puzzle

alt text

目标是重新排列瓷砖,使它们出现在自然位置。您一次只能滑动一个图块。拼图的每个可能状态是搜索图中的节点。

对于h(x)函数,我在所有图块中使用了图块与目标状态的错位的总和。在上面的图像中,5位于位置0,0,它属于位置1,0,因此它对h(x)函数贡献1。下一个区块是11,位于0,1,属于2,2,因此它对h(x)贡献3。等等。 编辑:我现在明白这就是所谓的“曼哈顿距离”或“taxicab distance”。

我一直在使用g(x)的步数。在我的实现中,对于状态图中的任何节点,g只是来自先前节点g的+1。

为了找到连续的节点,我只是检查在哪里可以移动拼图中的“洞”。显示的拼图状态(aka节点)有3个邻居:洞可以向北,向西或向东移动。

我的A *搜索有时会收敛到20s,有时是180s的解决方案,有时根本不收敛(等待10分钟或更长时间)。我认为我是合理的。我想知道我是否正确地模拟了g。换句话说,我的A *函数是否可能通过不是最短路径的路径到达图中的节点?

也许我没有等待足够长的时间?也许10分钟不够长?

对于完全随机的安排,(假设没有奇偶校验问题),A *解决方案将检查的平均排列数是多少?(请显示数学)

我将在我的代码中查找逻辑错误,但与此同时, 有小费吗?

(ps:它是用Javascript完成的)。

另外,不,这不是CompSci的功课。这只是个人探索的事情。我只是想学习Javascript。


编辑:我发现运行时间高度依赖于启发式。我从有人提到的文章中看到了10x因子应用于启发式,它让我想知道 - 为什么10x?为何线性?因为这是在javascript中完成的,所以我可以修改代码以使用当前正在考虑的节点动态更新html表。这允许我在算法进展时查看算法。使用常规的出租车距离启发式,我看到它没有收敛。

最上面有5个和12个,他们一直闲逛。我看到1,2,3,4爬进了最上面的一行,但随后它们会退出,其他数字会向上移动。我希望看到的是1,2,3,4爬到顶部,然后呆在那里。

我心想 - 这不是我亲自解决这个问题的方式。手动执行此操作,我解决了顶行,然后是2ne行,然后是第3行和第4行。

所以我调整了h(x)函数来对更高的行和“lefter”列进行更大的权重。结果是A *收敛得更快。它现在运行3分钟而不是“无限期”。通过我所谈到的“偷看”,我可以看到较小的数字爬到更高的行并留在那里。这不仅是正确的事情,而且运行得更快。

我正在尝试一系列变化。很明显,A *运行时对启发式非常敏感。目前我发现的最佳启发式使用dislocation * ((4-i) + (4-j))的总和,其中i和j是行和列,而位错是出租车距离。

我得到的结果中有一个有趣的部分:使用特定的启发式方法我很快找到了一条路径,但它显然不是最短的路径。我认为这是因为我正在加权启发式。在一个案例中,我在10s内获得了178步的路径。我自己的手动努力产生了87个动作的解决方案。 (超过10秒)。更多的调查需要保证。

所以结果是我看到它收敛必须更快,而且路径绝对不是最短的。我不得不考虑更多。


代码:

var stop = false; 
function Astar(start, goal, callback) {
    // start and goal are nodes in the graph, represented by 
    // an array of 16 ints.  The goal is:  [1,2,3,...14,15,0] 
    // Zero represents the hole. 

    // callback is a method to call when finished. This runs a long time, 
    // therefore we need to use setTimeout() to break it up, to avoid
    // the browser warning like "Stop running this script?"

    // g is the actual distance traveled from initial node to current node.
    // h is the heuristic estimate of distance from current to goal.
    stop = false;
    start.g = start.dontgo = 0;

    // calcHeuristic inserts an .h member into the array
    calcHeuristicDistance(start);

    // start the stack with one element
    var closed = [];       // set of nodes already evaluated.
    var open = [ start ];  // set of nodes to evaluate (start with initial node)

    var iteration = function() {
        if (open.length==0) {
            // no more nodes.  Fail. 
            callback(null);
            return;
        }
        var current = open.shift();  // get highest priority node

        // update the browser with a table representation of the 
        // node being evaluated
        $("#solution").html(stateToString(current));

        // check solution returns true if current == goal
        if (checkSolution(current,goal)) {
            // reconstructPath just records the position of the hole 
            // through each node
            var path= reconstructPath(start,current);
            callback(path);
            return;
        }

        closed.push(current);

        // get the set of neighbors.  This is 3 or fewer nodes.
        // (nextStates is optimized to NOT turn directly back on itself)
        var neighbors = nextStates(current, goal);

        for (var i=0; i<neighbors.length;  i++) {
            var n = neighbors[i];

            // skip this one if we've already visited it
            if (closed.containsNode(n)) continue;

            // .g, .h, and .previous get assigned implicitly when 
            // calculating neighbors.  n.g is nothing more than
            // current.g+1 ;

            // add to the open list
            if (!open.containsNode(n)) {
                // slot into the list, in priority order (minimum f first)
                open.priorityPush(n);
                n.previous = current;
            }
        }

        if (stop) {
            callback(null);
            return;
        }

        setTimeout(iteration, 1);
    };

    // kick off the first iteration
    iteration();

    return null;
}

8 个答案:

答案 0 :(得分:4)

A-star搜索将通过证明所有尚未解决的路径无法以比当前解决方案更少的移动来解决,从而找到最佳解决方案。您不是在寻找最佳解决方案,而是最快的解决方案。因此,您可以通过返回第一个解决方案来优化算法,通过加权低于启发函数的移动次数,启发式可以返回高估。

启发式函数本身通常最好由Manhattan distance和线性冲突建模。曼哈顿距离在其他答案和维基百科文章中得到了很好的解释,你似乎已经掌握了它。对于必须交换以达到解决方案的每对块,线性冲突为曼哈顿距离增加了两个。例如,如果一行包含“3 2 1 4”,则必须交换一个和三个,并且必须将一个行移动到另一行才能这样做。

使用模式数据库是一个选项,可以帮助您的搜索避免某些死胡同,并且这样做对15拼图的内存使用应该是可管理的。

答案 1 :(得分:4)

使用IDA *代替A *。你需要更少的内存。作为一种启发式方法,"Walking distance" developed by Ken'ichiro Takahashi更有效,尽管只使用25 kB的内存 Herehere是英文翻译。

答案 2 :(得分:2)

是的,这就是我听说过这个问题的方法。 g(x)是已发生的图块幻灯片的数量,h(x)是所有图块与其所需方格的总距离。在今天之前我没有看到任何使用过的方法(Manhattan heuristic),但只是发现了这个所谓的diagonal shortcut - 您可能想要查看它。

答案 3 :(得分:1)

您在测试数据中使用了什么?如果它是随机的,你将无法在一半的时间内解决这个难题。切换两个瓷砖是不可能的,同时将其余部分保持在相同的位置,因此如果你达到的几乎是最终位置,但有两个瓷砖互换,你不可能把它放到所需的位置,没有搜索算法可以成功终止。

在19世纪,美国拼图大师萨姆·洛伊德(Sam Loyd)以15和14的价格出售了这些玩具,并为任何能够展示切换瓷砖的解决方案的人提供了大奖(大概不是我所拥有的那个,一个小的螺丝刀)。在今天的法律环境中,我不知道他是否敢于。

一种可能性是尝试将其置于正确的配置或15-14配置中。

答案 4 :(得分:1)

我学到了什么

  • 显然这是well-known,但它不适合我:A *收敛对启发式函数非常敏感。
  • 如果我编写一个启发式,比其他行更重地加权前2行,它会更快地收敛,但路径通常要长得多。
  • 对于15平方拼图,我发现对角H(x)函数显示here比曼哈顿距离快得多。
  • 即使具有鼓励更快收敛的启发式功能,运行时间也存在很大差异。有时它会在10秒内找到路径。有时10分钟。有时更长。
  • 使用对角线启发式算法找到的路径中所需的移动次数范围为30-ish到110.

答案 5 :(得分:1)

我已经编程了一次这样的算法(windowsApp),我有以下经验

1)如果机器人使用(接近)最佳解决方案,那么它最有趣。 (对于人类观察者来说,不可能理解机器人如何“思考”,从混乱到秩序的交易是突然的)

2)如果你想找到最佳解决方案,你的h()函数必须低估真实距离。如果你高估它,你将找不到最佳选择。

3)潜在的状态空间很大,15!/ 2(10 ^ 12)。如果使用不良的启发式功能,您的数据集将远远超出主内存的大小,并且每次访问数据都需要多次访问光盘。如果发生这种情况,执行时间将是“无限的”。

答案 6 :(得分:0)

如果你先为中间目标射击,也许会更快收敛。例如,只对顶行和右行进行评分。将这些行放到适当的位置不需要很长时间,然后就可以解决剩余的3x3。

答案 7 :(得分:-2)

check this
import javax.swing.*; 
import java.awt.*;
import java.awt.event.*;
import java.lang.Object;

class Puzzle extends JPanel implements ActionListener
{
    JButton[] b = new JButton[16];
    Puzzle()
    {
        b[0] = new JButton("4");
        b[1] = new JButton("11");
        b[2] = new JButton("5");
        b[3] = new JButton("9");
        b[4] = new JButton("1");
        b[5] = new JButton("10");
        b[6] = new JButton("12");
        b[7] = new JButton("13");
        b[8] = new JButton("15");
        b[9] = new JButton("14");
        b[10] = new JButton("3");
        b[11] = new JButton("2"); 
        b[12] = new JButton("7");
        b[13] = new JButton("8");
        b[14] = new JButton("6");
        b[15] = new JButton("");
        GridLayout grid = new GridLayout(4,4);
        setLayout(grid);
        for(int i=0;i<16;i++)
            add(b[i]);
        for(int i=0;i<16;i++)
            b[i].addActionListener(this);
    }
    public void actionPerformed(ActionEvent e)
    {
        /*if(e.getSource()==b[11])
        {
            if(b[15].getText()=="")
            {
                b[15].setText("");
            }
        }
        else if(e.getSource()==b[3])
        {
            if(b[2].getText()=="")
            {
                b[2].setText("");
            }
        }*/
        for(int i=0;i<16;i++)
        {
            System.out.println(e.getSource());
            if(e.getSource()==b[i])
            {
                if(i==5 || i==6 || i==9 || i==10)
                {   
                    if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                else if(i==4 || i==8)
                {   
                    if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                else if(i==7 || i==11)
                {   
                    if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==0)
                {   
                    if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==3)
                {   
                    if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==15)
                {   
                    if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==12)
                {   
                    if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==1 || i==2)
                {   
                    if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }                   
                    else if(b[i+4].getText()=="")
                    {
                        b[i+4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
                if(i==13 || i==14)
                {   
                    if(b[i+1].getText()=="")
                    {
                        b[i+1].setText(b[i].getText());
                        b[i].setText("");
                    }
                    else if(b[i-1].getText()=="")
                    {
                        b[i-1].setText(b[i].getText());
                        b[i].setText("");
                    }                   
                    else if(b[i-4].getText()=="")
                    {
                        b[i-4].setText(b[i].getText());
                        b[i].setText("");
                    }
                }
            }
        }
        //System.out.println(e.getActionCommand());
        }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("15-Puzzle");             

        //frame.setContentPane(panel);

JComponent newContentPane = new Puzzle();
        //newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);





        //panel.add(button);  
        frame.setSize(400,400);


        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}