中级实训第三阶段Jigsaw启发式算法

中级实训第三阶段提出了如何用java,将打乱的拼图恢复到正确的位置的问题。并提示采用两种方法:

  • 广度搜索
  • 启发式算法

广度优先算法

广度优先算法属于盲目搜索算法,基本思想是将拼图移动的每种情况看成一个节点,从起始节点(拼图的起始状态)开始依次访问未访问过的邻接节点,直到寻找到结果节点(即拼图到达正确位置)时结束。这里不再赘述。
《中级实训第三阶段Jigsaw启发式算法》

启发式算法

这部分只要求完成一个估价函数,来估计每个节点的重要性。不同于盲目搜索,启发式算法会选择最有价值的节点来作为下一步移动,因此比广度优先算法要更加快捷。

首先是对于估价函数的确定

根据题目要求,n*n 拼图的状态由 长度为n+1 数组表示,第0号位置为空节点的位置,接下来的n位为内容从1到n*n的块的位置。
如:
《中级实训第三阶段Jigsaw启发式算法》

因此,确定三个估价函数:

  • 当前块的位置与正确位置的曼哈顿距离
  • 当前块的位置与正确位置的欧式距离
  • 后续不正确节点数

容易知道,第i号元素的位置为:

  • x:(i1)dimension x : ( i − 1 ) d i m e n s i o n 表示行
  • y:(i1)%dimension y : ( i − 1 ) % d i m e n s i o n 表示列

需要特别注意的是,x的计算结果表示行,因此计算行距时是拼图垂直方向的距离。同理,y表示列距,为拼图水平方向的距离

假设块当前在第i位置,正确位置为j
则曼哈顿距离和欧式距离分别为:
《中级实训第三阶段Jigsaw启发式算法》

因此,我将估价函数设为这三个函数的线性组合:

total=acirDis+blineDis+(10ab)s t o t a l = a ∗ c i r D i s + b ∗ l i n e D i s + ( 10 − a − b ) ∗ s

由函数可以知道,每个节点的total值越小,代表其价值更高。

调整参数a,b

确定参数可以有很多种算法。曾经尝试过使用SOR方法来迭代,使得完成一次拼图之后访问的所有节点数最少。显然,这里我将完成拼图所需要访问的所有节点当作cost函数值,但是cost的值必须在每次搜索完全部节点才能获得,因此无法写出明确的函数表达式。其次,由于参数值必须为整数int,而SOR要求所有参数和为1。因此这种方法没法实施。

所以最终采用了笨办法,通过随机产生参数值并进行多次ASearch测试,计算平均访问节点数,假如结果比上一次检验的结果好,则将当前参数替换旧参数。否则保留旧参数。
另外,在ASearch过程,由于每个节点都要调用一次估价函数,因此我直接为jigsaw类添加了两个变量paraA, paraB

调参函数

    public void changePar() {
        // TODO Auto-generated method stub
        int n = 10;
        int totalInit = 20000;
        while (n != 0) {
            int initA = paraA;
            int initB = paraB;
            int time = 4;    //表示每次检验进行搜索的次数
            this.paraA = (int)(Math.random()*10);
            this.paraB = (int)(Math.random()*10);
            int totalNum = 0;
            for (int i = 0; i < time; i ++) {
                searchedNodesNum = 0;
                JigsawNode dest = new JigsawNode(new int[]{25,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,0});
                JigsawNode start = Solution.scatter(dest, 1000);
                ASearch(start, dest);
                totalNum += searchedNodesNum;
            }
            System.out.printf("average: %d\n", totalNum/time);
            if(totalNum/time > totalInit) {
                paraA = initA;
                paraB = initB;
            }
            else {
                totalInit = totalNum/time;
            }
            n--;
            System.out.printf("current: %d\n", totalInit);
            System.out.printf("para1: %d, para2: %d \n", paraA, paraB);
        }

    }

估价函数

    public void estimateValue(JigsawNode jNode, int a, int b) {
        int s = 0; // 后续节点不正确的数码个数
        int dimension = JigsawNode.getDimension();
        for(int index =1 ; index<dimension*dimension; index++){
            if(jNode.getNodesState()[index]+1!=jNode.getNodesState()[index+1])
                s++;
        }
        int wrongPosition = 0;
        int lineDis = 0;  
        int cirDis = 0;  
        for(int i = 1; i <= dimension*dimension; i++){  
            for(int j = 1; j <= dimension*dimension; j++ ){  
                if(jNode.getNodesState()[i] != 0 && jNode.getNodesState()[i] == endJNode.getNodesState()[j]){  
                    wrongPosition++;
                    int x = Math.abs(((i-1)/dimension - (j-1)/dimension));
                    int y = Math.abs(((i-1)%dimension - (j-1)%dimension));
                    cirDis += ( x + y );   //x 行数 , y列数
                    lineDis += Math.sqrt( x*x + y*y);  
                    //cirDis曼哈顿距离,lineDis欧式距离
                    break;  
                }  
            }  
        }  
        //int total = 2*cirDis+lineDis+s;
        int total = a*cirDis+b*s+(10-a-b)*lineDis;
        jNode.setEstimatedValue(total);
    }

由于每次搜索需要较长时间,因此每次检验的迭代数设的较小,导致结果没有很好,只是作说明用。并且由于不能改动已有函数,因此每次迭代都会产生冗长的输出,此处只截取部分输出。
《中级实训第三阶段Jigsaw启发式算法》

《中级实训第三阶段Jigsaw启发式算法》

    原文作者:启发式算法
    原文地址: https://blog.csdn.net/huangbx_tx/article/details/80375194
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞