8/15数码问题的A*算法

题目描述

有以下的表格,然后每次与空白表格相邻的位置可以与其位置进行交换,求进行交换过程中的路径。
在这里插入图片描述

在这里插入图片描述

思路

这种题目可以使用A*算法来解决,具体的复杂度要看估价函数。

我这里使用了两种估价函数,虽然整体还是很垃圾,但是能够明显的感觉到不同估价函数对运行时间的影响。

另外就是,实现的算法中没有保存交换路径,留待解决。

具体的实现,我把表格封装成了一个对象,其中封装了移动,估价函数这些操作。

然后留给其他的就只是一个循环,不停的判断是否在OPEN表和CLOSED表中以及是否已经到达了结果 的位置。

可以继续优化的点,这个里面用到了排序,可以自己去实现一个排序列表,然后就不用每次都要进行排序,只需要根据相应的操作进行插入删除就好,这样应该还能再优化不少。

最后关于这两个例子都能运行出来结果,8数码应该几步就行,15数码我跑了40分钟,贼垃圾。

代码

8数码问题

package work;

import org.junit.Test;

import java.lang.reflect.Array;
import java.util.*;

/**
 * 8数码问题
 * @author modev
 * @date 2020/9/30  15:07
 */
public class EightDigitalProblem {
    
    @Test
    public void test() {
        Digital digital = new Digital(new int[][] {{2,8,3}, {1,6,4}, {7,0,5} });
        Digital target = new Digital(new int[][] {{1,2,3}, {8,0,4}, {7,6,5} });

        digital.setCount(target);
        digital.x = 2;
        digital.y = 1;

        target.flag = 0;
        target.x = 1;
        target.y = 1;

        start(digital, target);

    }

    public void start(Digital digital, Digital target) {

        LinkedList<Digital> open = new LinkedList<>();
        Set<Digital> closed = new HashSet<>();

        open.addFirst(digital);
        int count = 0;

        while (!open.isEmpty() && count < 30) {

            count++;
            Digital node = open.pollFirst();

            if (node.flag == 0 || target.equals(node)) {
                System.out.println(closed);
                System.out.println(node);
                System.out.println("结束" + "\t 一共进行了:" + count +"次");
                break;
            }

            if (closed.contains(node)) { continue; }
            Digital[] tmp = {node.up(), node.down(), node.left(), node.right()};
            for (Digital d : tmp) {
                if (d == null) { continue; }
                if (!closed.contains(d) && !open.contains(d)) {
                    d.setCount(target);
                    open.add(d);
                }
            }

            //排序
            Collections.sort(open);

            closed.add(node);
//            System.out.println(open);
        }
    }
}

/**
 * 节点工具类
 */
class Digital implements Comparable<Digital>{
    /**
     * 存储表格
     */
    int[][] board;
    /**
     * 存储和标准结果的差距
     */
    int flag;

    /**
     * 统计是第几步了
     */
    int step;
    /**
     * 存储空格的位置
     */
    int x;
    int y;

    Digital() {
        board = new int[3][3];
        flag = 8;
        x = 0;
        y = 0;
    }

    Digital(int[][] board) {
        this.board = board;
    }

    /**
     * 获取当前状态向上移动一位的结果
     * @return 新的对象,如果不存在则为null
     */
    Digital up() {
        if (x == 0) { return null; }

        Digital tmp = new Digital();
        for (int i = 0; i < 3; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
        }
        tmp.board[x][y] = tmp.board[x-1][y];
        tmp.board[x-1][y] = 0;
        tmp.x = this.x-1;
        tmp.y = this.y;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向下
     * @return
     */
    Digital down() {
        if (x == 2) { return null; }

        Digital tmp = new Digital();
        for (int i = 0; i < 3; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
        }
        tmp.board[x][y] = tmp.board[x+1][y];
        tmp.board[x+1][y] = 0;
        tmp.x = this.x+1;
        tmp.y = this.y;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向左
     * @return
     */
    Digital left() {
        if (y == 0) { return null; }

        Digital tmp = new Digital();
        for (int i = 0; i < 3; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
        }
        tmp.board[x][y] = tmp.board[x][y-1];
        tmp.board[x][y-1] = 0;
        tmp.x = this.x;
        tmp.y = this.y-1;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向右
     * @return
     */
    Digital right() {
        if (y == 2) { return null; }

        Digital tmp = new Digital();
        for (int i = 0; i < 3; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
        }
        tmp.board[x][y] = tmp.board[x][y+1];
        tmp.board[x][y+1] = 0;
        tmp.x = this.x;
        tmp.y = this.y+1;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 设置其与标准状态的差距的值
     * @param target 标准状态
     */
    void setCount(Digital target) {
        int count = 0;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (this.board[i][j] != target.board[i][j]) {
                    count++;
                }
            }
        }
        this.flag = count;
    }

    /**
     * 重写equals函数,只要两个对象中数据一样就相同
     * @param o 要判断的对象
     * @return 判断结果
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Digital digital = (Digital) o;

        boolean res = flag == digital.flag;
        for (int i = 0; i < 3 && res; i++) {
            res = Arrays.equals(board[i], digital.board[i]);
        }
        return res;
    }

    /**
     * 重写hashCode函数,只要表格相同,得到的hash值就相同
     * @return
     */
    @Override
    public int hashCode() {

        int result = Arrays.hashCode(board[0]) * 2;
        result += Arrays.hashCode(board[1]) * 3 + Arrays.hashCode(board[2]) * 7;
        return result;
    }

    /**
     * 因为要根据flag的值进行排序,所以就实现接口,重写函数
     * @param o 要对比的对象
     * @return 大于返回1,等于返回0,小于返回-1
     */
    @Override
    public int compareTo(Digital o) {
        return Integer.compare(this.flag+this.step, o.flag+o.step);
    }

    @Override
    public String toString() {
        return "Digital{" +
                "board=" + Arrays.toString(board[0]) + ", " + Arrays.toString(board[1])+ ", " + Arrays.toString(board[2]) +
                ", flag=" + flag +
                ", step=" + step +
                ", x=" + x +
                ", y=" + y +
                '}';
    }
}

15数码问题

package work;

import org.junit.Test;

import java.util.*;

/**
 * @author modev
 * @date 2020/10/6  14:16
 */
public class FifteenDigitalProblem {

    @Test
    public void test() {
        FifteenDigital digital = new FifteenDigital(new int[][] {{11,9,4,15}, {1,3,0,12}, {7,5,8,6},{13,2,10,14} });
        FifteenDigital target = new FifteenDigital(new int[][] {{1,2,3,4}, {5,6,7,8}, {9,10,11,12},{13,14,15,0} });

        digital.setCount(target);
        digital.x = 1;
        digital.y = 2;

        target.flag = 0;
        target.x = 3;
        target.y = 3;

        start(digital, target);

    }

    public void start(FifteenDigital digital, FifteenDigital target) {

        LinkedList<FifteenDigital> open = new LinkedList<>();
        Set<FifteenDigital> closed = new HashSet<>();

        open.addFirst(digital);
        int count = 0;

        while (!open.isEmpty()) {

            count++;
            FifteenDigital node = open.pollFirst();

            if (node.flag == 0 || target.equals(node)) {
                System.out.println(closed);
                System.out.println(node);
                System.out.println("结束" + "\t 一共进行了:" + count +"次");
                break;
            }

            if (closed.contains(node)) { continue; }
            FifteenDigital[] tmp = {node.up(), node.down(), node.left(), node.right()};
            for (FifteenDigital d : tmp) {
                if (d == null) { continue; }
                if (!closed.contains(d) && !open.contains(d)) {
                    d.setCount(target);
                    open.add(d);
                }
            }

            //排序
            Collections.sort(open);

            closed.add(node);
            System.out.println(count);
        }
    }
}

/**
 * 节点工具类
 */
class FifteenDigital implements Comparable<FifteenDigital>{
    /**
     * 存储表格
     */
    int[][] board;
    /**
     * 存储和标准结果的差距
     */
    int flag;

    /**
     * 统计是第几步了
     */
    int step;
    /**
     * 存储空格的位置
     */
    int x;
    int y;

    FifteenDigital() {
        board = new int[4][4];
        flag = 15;
        x = 0;
        y = 0;
    }

    FifteenDigital(int[][] board) {
        this.board = board;
    }

    /**
     * 获取当前状态向上移动一位的结果
     * @return 新的对象,如果不存在则为null
     */
    FifteenDigital up() {
        if (x == 0) { return null; }

        FifteenDigital tmp = new FifteenDigital();
        for (int i = 0; i < 4; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
        }
        tmp.board[x][y] = tmp.board[x-1][y];
        tmp.board[x-1][y] = 0;
        tmp.x = this.x-1;
        tmp.y = this.y;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向下
     * @return
     */
    FifteenDigital down() {
        if (x == 3) { return null; }

        FifteenDigital tmp = new FifteenDigital();
        for (int i = 0; i < 4; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
        }
        tmp.board[x][y] = tmp.board[x+1][y];
        tmp.board[x+1][y] = 0;
        tmp.x = this.x+1;
        tmp.y = this.y;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向左
     * @return
     */
    FifteenDigital left() {
        if (y == 0) { return null; }

        FifteenDigital tmp = new FifteenDigital();
        for (int i = 0; i < 4; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
        }
        tmp.board[x][y] = tmp.board[x][y-1];
        tmp.board[x][y-1] = 0;
        tmp.x = this.x;
        tmp.y = this.y-1;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 向右
     * @return
     */
    FifteenDigital right() {
        if (y == 3) { return null; }

        FifteenDigital tmp = new FifteenDigital();
        for (int i = 0; i < 4; i++) {
            System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
        }
        tmp.board[x][y] = tmp.board[x][y+1];
        tmp.board[x][y+1] = 0;
        tmp.x = this.x;
        tmp.y = this.y+1;
        tmp.step = this.step+1;

        return tmp;
    }

    /**
     * 设置其与标准状态的差距的值
     * @param target 标准状态
     */
    void setCount(FifteenDigital target) {
        this.flag = setManhattanDistanceCount(target);
    }

    /**
     * 简单的计算有几个不同的数字
     * @param target
     * @return 不同数字的数量
     */
    int setSimpleCount(FifteenDigital target) {
        int count = 0;

        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (this.board[i][j] != target.board[i][j]) {
                    count++;
                }
            }
        }
       return count;
    }

    /**
     * 使用曼哈顿距离计算差值
     * @param target
     * @return 曼哈顿距离下的结果
     */
    int setManhattanDistanceCount(FifteenDigital target) {
        int count = 0;

        int[][] maps = new int[16][2];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                maps[target.board[i][j]][0] = i;
                maps[target.board[i][j]][1] = j;
            }
        }

        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                int tmp = Math.abs(maps[this.board[i][j]][0] - i) + Math.abs(maps[this.board[i][j]][1] - j);
                count += tmp;
            }
        }

        return count;

    }

    /**
     * 重写equals函数,只要两个对象中数据一样就相同
     * @param o 要判断的对象
     * @return 判断结果
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        FifteenDigital digital = (FifteenDigital) o;

        boolean res = flag == digital.flag;
        for (int i = 0; i < 4 && res; i++) {
            res = Arrays.equals(board[i], digital.board[i]);
        }
        return res;
    }

    /**
     * 重写hashCode函数,只要表格相同,得到的hash值就相同
     * @return
     */
    @Override
    public int hashCode() {

        int result = Arrays.hashCode(board[0]) * 2;
        result += Arrays.hashCode(board[1]) * 3 + Arrays.hashCode(board[2]) * 7;

        result += Arrays.hashCode(board[3]) * 11;

        result = Integer.hashCode(result) * 17;
        return result;
    }

    /**
     * 因为要根据flag的值进行排序,所以就实现接口,重写函数
     * @param o 要对比的对象
     * @return 大于返回1,等于返回0,小于返回-1
     */
    @Override
    public int compareTo(FifteenDigital o) {
        return Integer.compare(this.flag+this.step, o.flag+o.step);
    }

    @Override
    public String toString() {
        return "FifteenDigital{" +
                "board=" + Arrays.toString(board[0]) + ", " + Arrays.toString(board[1])+ ", " + Arrays.toString(board[2]) +", " + Arrays.toString(board[3]) +
                ", flag=" + flag +
                ", step=" + step +
                ", x=" + x +
                ", y=" + y +
                '}';
    }
}

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值