--- title: 8パズル問題 tags: [] categories: ["Programming", "Algorithm", "Search", "EightPuzzle"] date: 2011-07-19T15:45:21Z updated: 2011-07-19T15:45:21Z --- 久々に探索を復習。8パズル問題を深さ優先探索、幅優先探索、A*探索で解いてみた。適当Java実装なり DFSはスタック、BFSはキュー、A*はPQを使うのがポイントか? import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; import java.util.Stack; public class EightPuzzle { public static class Node implements Comparable { final int board[]; int depth; Node prev; Evaluator evaluator; public Node(int... state) { if (state == null || state.length != 9) { throw new IllegalArgumentException("illegal"); } this.board = state; depth = 0; prev = null; } private void swap(int x[], int i, int j) { int t = x[i]; x[i] = x[j]; x[j] = t; } public List movableNodes() { int pos = -1; for (int i = 0; i < board.length; i++) { if (board[i] == 0) { pos = i; break; } } int x = pos / 3; int y = pos % 3; List states = new ArrayList(); if (x >= 1) { int[] u = board.clone(); swap(u, pos, pos - 3); states.add(new Node(u)); } if (x <= 1) { int[] d = board.clone(); swap(d, pos, pos + 3); states.add(new Node(d)); } if (y >= 1) { int[] l = board.clone(); swap(l, pos, pos - 1); states.add(new Node(l)); } if (y <= 1) { int[] r = board.clone(); swap(r, pos, pos + 1); states.add(new Node(r)); } for (Node n : states) { n.prev = this; n.depth = depth + 1; n.evaluator = evaluator; } return states; } public void printTrace() { Node n = this; Deque trace = new ArrayDeque(); while (n != null) { trace.addFirst(n); n = n.prev; } System.out.println(trace); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(board); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Node other = (Node) obj; if (!Arrays.equals(board, other.board)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 3; i++) { sb.append(System.getProperty("line.separator")); sb.append("["); for (int j = 0; j < 3; j++) { if (j != 0) { sb.append(", "); } sb.append(board[i * 3 + j]); } sb.append("]"); } sb.append(System.getProperty("line.separator")); return sb.toString(); } public int compareTo(Node o) { if (evaluator != null) { int e1 = depth + evaluator.evaluate(this); int e2 = o.depth + evaluator.evaluate(o); if (e1 < e2) { return -1; } else if (e1 > e2) { return 1; } else { return 0; } } return 0; } } public static interface Evaluator { int evaluate(Node node); } // 深さ優先探索 public static Node depthFirstSearch(Node init, Node goal) { Set closed = new HashSet(); Stack open = new Stack(); open.add(init); int maxDepth = 9; while (!open.isEmpty()) { Node n = open.pop(); closed.add(n); for (Node next : n.movableNodes()) { if (closed.contains(next)) { continue; } if (next.equals(goal)) { System.out.println("goal"); System.out.println("depth=" + next.depth); System.out.println("closed=" + closed.size()); return next; } if (next.depth < maxDepth) { open.add(next); } } } return null; } // 幅優先探索 public static Node breadthFirstSearch(Node init, Node goal) { Set closed = new HashSet(); Queue open = new LinkedList(); open.add(init); while (!open.isEmpty()) { Node n = open.poll(); closed.add(n); for (Node next : n.movableNodes()) { if (closed.contains(next)) { continue; } if (next.equals(goal)) { System.out.println("goal"); System.out.println("depth=" + next.depth); System.out.println("closed=" + closed.size()); return next; } open.add(next); } } return null; } // A*探索 public static Node aStarSearch(Node init, Node goal, Evaluator evaluator) { Map closed = new HashMap(); init.evaluator = evaluator; PriorityQueue open = new PriorityQueue(); open.add(init); while (!open.isEmpty()) { Node n = open.poll(); closed.put(n.hashCode(), n); if (n.equals(goal)) { System.out.println("goal"); System.out.println("depth=" + n.depth); System.out.println("closed=" + closed.size()); return n; } for (Node next : n.movableNodes()) { Node prior = closed.get(next.hashCode()); if (prior != null) { if (evaluator.evaluate(next) < evaluator.evaluate(prior)) { closed.remove(next.hashCode()); open.add(next); } } else { open.add(next); } } } return null; } public static void main(String[] args) { Node init = new Node(8, 1, 3, 0, 4, 5, 2, 7, 6); final Node goal = new Node(1, 2, 3, 8, 0, 4, 7, 6, 5); { System.out.println("== DFS =="); Node result = depthFirstSearch(init, goal); if (result != null) { result.printTrace(); } else { System.out.println("NG"); } } { System.out.println("== BFS =="); Node result = breadthFirstSearch(init, goal); if (result != null) { result.printTrace(); } else { System.out.println("NG"); } } { System.out.println("== A* =="); Node result = aStarSearch(init, goal, new Evaluator() { // いい加減実装なヒューリスティック関数 int home[][]; { home = new int[goal.board.length][2]; for (int i = 0; i < goal.board.length; i++) { int e = goal.board[i]; home[e] = new int[2]; home[e][0] = i / 3; home[e][1] = i % 3; } } public int evaluate(Node node) { int s = 0; for (int i = 0; i < node.board.length; i++) { int e = node.board[i]; // ゴールまでのマンハッタン距離 s = s + Math.abs(home[e][0] - i / 3) + Math.abs(home[e][1] - i % 3); } return s; } }); if (result != null) { result.printTrace(); } else { System.out.println("NG"); } } } } 実行結果 適当な割にはA*が良い結果 == DFS == goal depth=9 closed=161 [ [8, 1, 3] [0, 4, 5] [2, 7, 6] , [8, 1, 3] [2, 4, 5] [0, 7, 6] , [8, 1, 3] [2, 4, 5] [7, 0, 6] , [8, 1, 3] [2, 4, 5] [7, 6, 0] , [8, 1, 3] [2, 4, 0] [7, 6, 5] , [8, 1, 3] [2, 0, 4] [7, 6, 5] , [8, 1, 3] [0, 2, 4] [7, 6, 5] , [0, 1, 3] [8, 2, 4] [7, 6, 5] , [1, 0, 3] [8, 2, 4] [7, 6, 5] , [1, 2, 3] [8, 0, 4] [7, 6, 5] ] == BFS == goal depth=9 closed=242 [ [8, 1, 3] [0, 4, 5] [2, 7, 6] , [8, 1, 3] [2, 4, 5] [0, 7, 6] , [8, 1, 3] [2, 4, 5] [7, 0, 6] , [8, 1, 3] [2, 4, 5] [7, 6, 0] , [8, 1, 3] [2, 4, 0] [7, 6, 5] , [8, 1, 3] [2, 0, 4] [7, 6, 5] , [8, 1, 3] [0, 2, 4] [7, 6, 5] , [0, 1, 3] [8, 2, 4] [7, 6, 5] , [1, 0, 3] [8, 2, 4] [7, 6, 5] , [1, 2, 3] [8, 0, 4] [7, 6, 5] ] == A* == goal depth=9 closed=15 [ [8, 1, 3] [0, 4, 5] [2, 7, 6] , [8, 1, 3] [2, 4, 5] [0, 7, 6] , [8, 1, 3] [2, 4, 5] [7, 0, 6] , [8, 1, 3] [2, 4, 5] [7, 6, 0] , [8, 1, 3] [2, 4, 0] [7, 6, 5] , [8, 1, 3] [2, 0, 4] [7, 6, 5] , [8, 1, 3] [0, 2, 4] [7, 6, 5] , [0, 1, 3] [8, 2, 4] [7, 6, 5] , [1, 0, 3] [8, 2, 4] [7, 6, 5] , [1, 2, 3] [8, 0, 4] [7, 6, 5] ]