A*算法解决九宫重排(代码)

A*算法解决九宫重排(代码)

package 九宫重排;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;

public class Main implements Comparable {
private int[] num = new int[9];
//估计函数f(n):从起始状态到目标的最小估计值
private int evaluation;
//d(n):当前的深度,即走到当前状态的步骤
private int depth;
//启发函数 h(n):到目标的最小估计(记录和目标状态有多少个数不同)
private int misposition;
//当前状态的父状态
private Main parent;
//保存最终路径
private ArrayList answer = new ArrayList();

public int[] getNum() {
    return num;
}

public void setNum(int[] num) {
    this.num = num;
}

public int getDepth() {
    return depth;
}

public void setDepth(int depth) {
    this.depth = depth;
}

public int getEvaluation() {
    return evaluation;
}

public void setEvaluation(int evaluation) {
    this.evaluation = evaluation;
}

public int getMisposition() {
    return misposition;
}

public void setMisposition(int misposition) {
    this.misposition = misposition;
}

public Main getParent() {
    return parent;
}

public void setParent(Main parent) {
    this.parent = parent;
}

/**
 * 判断当前状态是否为目标状态
 *
 * @param target
 * @return
 */
public boolean isTarget(Main target) {
    return Arrays.equals(getNum(), target.getNum());
}

/**
 * 求估计函数f(n) = g(n)+h(n);
 * 初始化状态信息
 *
 * @param target
 */
public void init(Main target) {
    int temp = 0;
    for (int i = 0; i < 9; i++) {
        if (num[i] != target.getNum()[i])
            //记录当前节点与目标节点差异的度量
            temp++;
    }
    this.setMisposition(temp);
    if (this.getParent() == null) {
        //初始化步数(深度)
        this.setDepth(0);
    } else {
        //记录步数
        this.depth = this.parent.getDepth() + 1;
    }
    //返回当前状态的估计值
    this.setEvaluation(this.getDepth() + this.getMisposition());
}

/**
 * 求逆序值并判断是否有解,逆序值同奇或者同偶才有解
 *
 * @param target
 * @return 有解:true 无解:false
 */
public boolean isSolvable(Main target) {
    int reverse = 0;
    for (int i = 0; i < 9; i++) {
        //遇到0跳过
        for (int j = 0; j < i; j++) {
            if (num[j] > num[i] && num[j] != 0 && num[i] != 0)
                reverse++;
            if (target.getNum()[j] > target.getNum()[i] && target.getNum()[j] != 0 && target.getNum()[i] != 0)
                reverse++;
        }
    }
    if (reverse % 2 == 0)
        return true;
    return false;
}

/**
 * 对每个子状态的f(n)进行由小到大排序
 */
@Override
public int compareTo(Object o) {
    Main c = (Main) o;
    //默认排序为f(n)由小到大排序
    return this.evaluation - c.getEvaluation();
}

/**
 * @return 返回0在八数码中的位置
 */
public int getZeroPosition() {
    int position = -1;
    for (int i = 0; i < 9; i++) {
        if (this.num[i] == 0) {
            position = i;
        }
    }
    return position;
}

/**
 * 去重,当前状态不重复返回-1
 *
 * @param open 状态集合
 * @return 判断当前状态是否存在于open表中
 */
public int isContains(ArrayList<Main> open) {
    for (int i = 0; i < open.size(); i++) {
        if (Arrays.equals(open.get(i).getNum(), getNum())) {
            return i;
        }
    }
    return -1;
}

/**
 * 一维数组
 *
 * @return
 */
public boolean isMoveUp() {
    int position = getZeroPosition();
    if (position <= 2) {
        return false;
    }
    return true;
}

/**
 * @return
 */
public boolean isMoveDown() {
    int position = getZeroPosition();
    if (position >= 6) {
        return false;
    }
    return true;
}

/**
 * @return
 */
public boolean isMoveLeft() {
    int position = getZeroPosition();
    if (position % 3 == 0) {
        return false;
    }
    return true;
}

/**
 * @return
 */
public boolean isMoveRight() {
    int position = getZeroPosition();
    if ((position) % 3 == 2) {
        return false;
    }
    return true;
}

/**
 * @param
 * @return 返回移动后的状态
 */
public Main moveUp(int move) {
    Main temp = new Main();
    int[] tempnum = num.clone();
    temp.setNum(tempnum);
    //0的位置
    int position = getZeroPosition();
    //与0换位置的元素的位置
    int p = 0;
    switch (move) {
        case 0:
            p = position - 3;
            temp.getNum()[position] = num[p];
            break;
        case 1:
            p = position + 3;
            temp.getNum()[position] = num[p];
            break;
        case 2:
            p = position - 1;
            temp.getNum()[position] = num[p];
            break;
        case 3:
            p = position + 1;
            temp.getNum()[position] = num[p];
            break;
    }
    temp.getNum()[p] = 0;
    return temp;
}

/**
 * 按照3*3格式输出
 */
public void print() {
    for (int i = 0; i < 9; i++) {
        if (i % 3 == 2) {
            System.out.println(this.num[i]);
        } else {
            System.out.print(this.num[i] + "  ");
        }
    }
}

/**
 * 将最终答案路径保存下来并输出
 */
public void printRoute() {
    Main temp = null;
    int count = 0;
    temp = this;
    System.out.println("----------开始移动----------");
    while (temp != null) {
        answer.add(temp);
        temp = temp.getParent();
        count++;
    }
    for (int i = answer.size() - 1; i >= 0; i--) {
        answer.get(i).print();
        System.out.println("--------------------");
    }
    System.out.println("最小移动步数:" + (count - 1));
}

/**
 * @param open   open表
 * @param close  close表
 * @param parent 父状态
 * @param target 目标状态
 */
public void operation(ArrayList<Main> open, ArrayList<Main> close, Main parent, Main target) {
    //判断是否在close表中
    if (this.isContains(close) == -1) {
        //获取在open表中的位置
        int position = this.isContains(open);
        //判断是否在open表中
        if (position == -1) {
            //指明它的父状态
            this.parent = parent;
            //计算它的估计值
            this.init(target);
            //把它添加进open表
            open.add(this);
        } else {
            //跟已存在的状态作比较,如果它的步数较少则是较优解
            if (this.getDepth() < open.get(position).getDepth()) {
                //把已经存在的相同状态替换掉
                open.remove(position);
                this.parent = parent;
                this.init(target);
                open.add(this);
            }
        }
    }
}

public static void main(String args[]) {
    //定义open表
    ArrayList<Main> open = new ArrayList<Main>();
    //定义close表
    ArrayList<Main> close = new ArrayList<Main>();
    Main start = new Main();
    Main target = new Main();

    Scanner s = new Scanner(System.in);
    int stnum[] = new int[9];
    int tanum[] = new int[9];
    System.out.println("请输入初始状态:");
    for (int i = 0; i < 9; i++) {
        stnum[i] = s.nextInt();
    }
    System.out.println("请输入目标状态:");
    for (int j = 0; j < 9; j++) {
        tanum[j] = s.nextInt();
    }
    s.close();

    start.setNum(stnum);
    target.setNum(tanum);
    long startTime = System.currentTimeMillis();
    if (start.isSolvable(target)) {
        //初始化初始状态
        start.init(target);
        open.add(start);
        while (open.isEmpty() == false) {
            //按照evaluation的值排序
            Collections.sort(open);
            //从open表中取出最小估值的状态并移出open表
            Main best = open.get(0);
            open.remove(0);
            close.add(best);

            if (best.isTarget(target)) {
                //输出
                best.printRoute();
                long end = System.currentTimeMillis();
                System.out.println("程序运行 " + (end - startTime) + " ms");
                System.exit(0);
            }

            int move;
            //由best状态进行扩展并加入到open表中
            //0的位置上移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
            //判断是否可以上移
            if (best.isMoveUp()) {
                //上移标记
                move = 0;
                //best的一个子状态
                Main up = best.moveUp(move);
                up.operation(open, close, best, target);
            }
            //0的位置下移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
            if (best.isMoveDown()) {
                move = 1;
                Main down = best.moveUp(move);
                down.operation(open, close, best, target);
            }
            //0的位置左移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
            if (best.isMoveLeft()) {
                move = 2;
                Main left = best.moveUp(move);
                left.operation(open, close, best, target);
            }
            //0的位置右移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
            if (best.isMoveRight()) {
                move = 3;
                Main right = best.moveUp(move);
                right.operation(open, close, best, target);
            }
        }
    } else {
        System.out.println("目标状态不可达");
    }
}

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
九宫重排问题是一种经典的搜索问题,可以使用A*算法解决。下面是使用C++实现A*算法解决九宫重排问题的代码示例: ```c++ #include <iostream> #include <queue> #include <vector> #include <map> using namespace std; const int N = 3; // 九宫格大小 const int MAX_STATE = 1e5; // 最大状态数 struct State { int a[N][N]; // 九宫格 int x, y; // 空格位置 int f, g, h; // f = g + h int id; // 状态编号 bool operator < (const State& rhs) const { return f > rhs.f; // 优先队列按 f 值从小到大排序 } }; int dx[4] = {-1, 0, 1, 0}; // 方向数组 int dy[4] = {0, 1, 0, -1}; int start[N][N] = { // 初始状态 {2, 8, 3}, {1, 6, 4}, {7, 0, 5} }; int goal[N][N] = { // 目标状态 {1, 2, 3}, {8, 0, 4}, {7, 6, 5} }; int get_h(const int a[][N]) { // 估价函数 int res = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (a[i][j] == 0) continue; int x = (a[i][j] - 1) / N; int y = (a[i][j] - 1) % N; res += abs(x - i) + abs(y - j); } } return res; } int state_id; // 状态编号 map<int, int> vis; // 判重数组 State S[MAX_STATE]; // 状态数组 int get_id(const int a[][N]) { // 获取状态编号 int res = 0, p = 1; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { res += a[i][j] * p; p *= 10; } } return res; } void print(const State& s) { // 打印状态 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { cout << s.a[i][j] << " "; } cout << endl; } } bool is_goal(const State& s) { // 判断是否为目标状态 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (s.a[i][j] != goal[i][j]) return false; } } return true; } void A_star() { // A*算法 priority_queue<State> q; state_id = 0; vis.clear(); State start_state; start_state.x = 1, start_state.y = 2; // 空格位置 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { start_state.a[i][j] = start[i][j]; } } start_state.g = 0; start_state.h = get_h(start_state.a); start_state.f = start_state.g + start_state.h; start_state.id = state_id++; S[start_state.id] = start_state; q.push(start_state); vis[get_id(start_state.a)] = start_state.id; while (!q.empty()) { State t = q.top(); q.pop(); if (is_goal(t)) { cout << "找到目标状态" << endl; print(t); return; } for (int k = 0; k < 4; k++) { int nx = t.x + dx[k], ny = t.y + dy[k]; if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue; State s = t; s.x = nx, s.y = ny; swap(s.a[t.x][t.y], s.a[nx][ny]); s.h = get_h(s.a); s.g = t.g + 1; s.f = s.g + s.h; int id = get_id(s.a); if (vis.count(id) == 0) { s.id = state_id++; S[s.id] = s; q.push(s); vis[id] = s.id; } else { int pre_id = vis[id]; if (S[pre_id].f > s.f) { S[pre_id] = s; vis[id] = s.id; q.push(s); } } } } } int main() { A_star(); return 0; } ``` A*算法的核心是估价函数,本例中使用的是曼哈顿距离,即当前状态与目标状态每个数码所在位置的曼哈顿距离之和。在代码实现中,使用了优先队列来维护搜索过程中的状态集合,采用了判重和状态编号的方法来优化算法效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值