Algorithm 关于将A*与15方块拼图结合使用的问题
我正试着为一家公司建立一个 目标是重新排列瓷砖,使其显示在其自然位置。一次只能滑动一个磁贴。谜题的每个可能状态都是搜索图中的一个节点 对于h(x)函数,我使用了所有分片的总和,分片与目标状态的错位。在上图中,5位于位置0,0,它属于位置1,0,因此它为h(x)函数贡献了1。下一个瓷砖是11,位于0,1,属于2,2,因此它对h(x)贡献了3。等等编辑:我现在明白了这就是他们所谓的“曼哈顿距离”或“曼哈顿距离” 我一直在使用g(x)的步长计数。在我的实现中,对于状态图中的任何节点,g与前一个节点的g仅相差+1 为了找到连续的节点,我只需检查在哪里可以移动拼图中的“洞”。显示的拼图状态(又名节点)有3个邻居:洞可以向北、向西或向东移动 我的A*搜索有时在20秒内收敛到一个解决方案,有时在180秒内收敛,有时根本不收敛(等待10分钟或更长时间)。我认为h是合理的。我想知道我是否正确地建模了g。换句话说,我的A*函数是否可能通过非最短路径的路径到达图中的节点 也许我等的时间不够长?也许10分钟不够长 对于完全随机排列(假设没有奇偶校验问题),a*解决方案将检查的平均排列数是多少?(请显示数学) 我将在代码中查找逻辑错误,但同时, 有什么建议吗 (附言:这是用Javascript完成的) 还有,不,这不是作业。这只是个人的探索。我只是想学习JavascriptAlgorithm 关于将A*与15方块拼图结合使用的问题,algorithm,graph,a-star,Algorithm,Graph,A Star,我正试着为一家公司建立一个 目标是重新排列瓷砖,使其显示在其自然位置。一次只能滑动一个磁贴。谜题的每个可能状态都是搜索图中的一个节点 对于h(x)函数,我使用了所有分片的总和,分片与目标状态的错位。在上图中,5位于位置0,0,它属于位置1,0,因此它为h(x)函数贡献了1。下一个瓷砖是11,位于0,1,属于2,2,因此它对h(x)贡献了3。等等编辑:我现在明白了这就是他们所谓的“曼哈顿距离”或“曼哈顿距离” 我一直在使用g(x)的步长计数。在我的实现中,对于状态图中的任何节点,g与前一个节点
编辑:我发现运行时高度依赖于启发式。我看到有人提到的文章将10倍因子应用于启发式,这让我想知道——为什么是10倍?为什么是线性的?因为这是在javascript中完成的,所以我可以修改代码来动态更新html表,其中包含当前正在考虑的节点。这让我得以在算法进行的过程中窥视它。通过常规的出租车距离启发,我看到它无法收敛 最上面一排有5个和12个,他们一直在闲逛。我会看到1,2,3,4爬到最上面一行,但是他们会退出,其他数字会上升。我希望看到的是1,2,3,4爬到顶端,然后留在那里 我想,这不是我个人解决问题的方式。手动执行此操作时,我会同时求解第一行、第二行、第三行和第四行 因此,我调整了h(x)函数,使更高的行和“左”列的权重更大。结果是A*收敛得更快。它现在在3分钟内运行,而不是“无限期”。通过我提到的“偷看”,我可以看到较小的数字爬到较高的行并停留在那里。这看起来不仅是正确的,而且运行速度更快 我正在尝试一系列的变化。很明显,A*运行时对启发式非常敏感。目前,我发现最好的启发式方法是使用位错*((4-I)+(4-j))的总和,其中I和j是行和列,位错是出租车距离 我得到的结果中有一个有趣的部分:使用特定的启发式方法,我很快找到了一条路径,但它显然不是最短路径。我认为这是因为我对启发式进行加权。在一个例子中,我得到了10秒内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;
}
var-stop=false;
函数Astar(开始、目标、回调){
//开始和目标是图中的节点,由
//16整数的数组。目标是:[1,2,3,…14,15,0]
//零代表洞。
//callback是一个在完成时调用的方法。它会运行很长时间,
//因此,我们需要使用setTimeout()来分解它,以避免
//浏览器警告,如“停止运行此脚本?”
//g是从初始节点到当前节点的实际移动距离。
//h是从当前到目标距离的启发式估计。
停止=错误;
start.g=start.dontgo=0;
//calcHeuristic在数组中插入一个.h成员
calcHeuristicDistance(开始);
//用一个元素开始堆栈
var closed=[];//已计算的节点集。
var open=[start];//要计算的节点集(从初始节点开始)
var迭代=函数(){
if(open.length==0){
//没有更多节点。失败。
回调(空);
返回;
}
var current=open.shift();//获取最高优先级节点
//使用浏览器的表格表示形式更新浏览器
//正在评估的节点
$(“#解决方案”).html(stateToString(当前));
//如果当前==目标,则检查解决方案返回true
if(检查解决方案(当前、目标)){
//重建路径只记录孔的位置
//通过每个节点
var路径=重建路径(开始,当前);
回调(路径);
返回;
}
关闭。推送(电流);
//获取邻居集。这是3个或更少的节点。
//(nextStates经过优化,不会直接打开自身)
var邻居=下一个状态(当前、目标);
(弗吉尼亚州)
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);
}
}