0

経路探索の問題に A* アルゴリズムを実装しようとしています。

10 回中 9 回のように機能しますが、いくつかの時点で (おそらく) 無限ループが発生し、プログラムは最適なパスを見つけられません。なぜそれが起こるのか分かりますか?

A*:

import java.util.*;


public abstract class AStar<T>
{
            private class Path implements Comparable{
                            public T point;
                            public Double f;
                            public Double g;
                            public Path parent;


                            public Path(){
                                            parent = null;
                                            point = null;
                                            g = f = 0.0;
                            }


                            public Path(Path p){
                                            this();
                                            parent = p;
                                            g = p.g;
                                            f = p.f;
                            }


                            public int compareTo(Object o){
                                            Path p = (Path)o;
                                            return (int)(f - p.f);
                            }


                            public T getPoint(){
                                            return point;
                            }


                            public void setPoint(T p){
                                            point = p;
                            }
            }


            protected abstract boolean isGoal(T node);


            protected abstract Double g(T from, T to);


            protected abstract Double h(T from, T to);



            protected abstract List<T> generateSuccessors(T node);


            private PriorityQueue<Path> paths;
            private HashMap<T, Double> mindists;
            private Double lastCost;
            private int expandedCounter;


            public int getExpandedCounter(){
                            return expandedCounter;
            }


            public AStar(){
                            paths = new PriorityQueue<Path>();
                            mindists = new HashMap<T, Double>();
                            expandedCounter = 0;
                            lastCost = 0.0;
            }



            protected Double f(Path p, T from, T to){
                            Double g =  g(from, to) + ((p.parent != null) ? p.parent.g : 0.0);
                            Double h = h(from, to);

                            p.g = g;
                            p.f = g + h;

                            return p.f;
            }


            private void expand(Path path){
                            T p = path.getPoint();
                            Double min = mindists.get(path.getPoint());


                            if(min == null || min.doubleValue() > path.f.doubleValue())
                                            mindists.put(path.getPoint(), path.f);
                            else
                                            return;

                            List<T> successors = generateSuccessors(p);

                            for(T t : successors){
                                            Path newPath = new Path(path);
                                            newPath.setPoint(t);
                                            f(newPath, path.getPoint(), t);
                                            paths.offer(newPath);
                            }

                            expandedCounter++;
            }

            public Double getCost(){
                            return lastCost;
            }

            public List<T> compute(T start){
                            try{
                                            Path root = new Path();
                                            root.setPoint(start);

                                            /* Needed if the initial point has a cost.  */
                                            f(root, start, start);

                                            expand(root);

                                            for(;;){
                                                            Path p = paths.poll();

                                                            if(p == null){
                                                                            lastCost = Double.MAX_VALUE;
                                                                            return null;
                                                            }

                                                            T last = p.getPoint();

                                                            lastCost = p.g;

                                                            if(isGoal(last)){
                                                                            LinkedList<T> retPath = new LinkedList<T>();

                                                                            for(Path i = p; i != null; i = i.parent){
                                                                                            retPath.addFirst(i.getPoint());
                                                                            }

                                                                            return retPath;
                                                            }
                                                            expand(p);
                                            }
                            }
                            catch(Exception e){
                                            e.printStackTrace();
                            }
                            return null;

            }

}

そして、メインのパスファインディング クラス:

import java.util.*;

public class PathFinder extends AStar<PathFinder.Node>
{
            private int[][] map;
            private int endx;
            private int endy;

            public static class Node{
                            public int x;
                            public int y;
                            Node(int x, int y){
                                            this.x = x; 
                                            this.y = y;
                            }
                           public String toString(){
                                            return "(" + x + ", " + y + ") ";
                            }
                            public int getX(){
                                return x;
                            }
                            public int getY(){
                                return y;
                            }
}               public PathFinder(int[][] map, int endx, int endy){
                            this.map = map;
                            this.endx=endx;
                            this.endy=endy;
            }

            protected boolean isGoal(Node node){
                            return (node.x == endx) && (node.y == endy);
            }

            protected Double g(Node from, Node to){

                            if(from.x == to.x && from.y == to.y){

                           //   System.out.println("To x1 " + to.x);
                         //     System.out.println("To y1 " + to.y);
                                            return 0.0;}

                            if(map[to.y][to.x] == 1){
                                //System.out.println("To x2 " + to.x);
                            //  System.out.println("To y2 " + to.y);

                                            return 1.0;}

                            return Double.MAX_VALUE;
            }

            protected Double h(Node from, Node to){

                           return new Double(Math.abs(endx - to.x) + Math.abs(endy - to.y));
            }

            protected List<Node> generateSuccessors(Node node){
                            List<Node> ret = new LinkedList<Node>();
                            int x = node.x;
                            int y = node.y;
                            if(y < map[0].length-1 && map[y+1][x] == 1)
                                            ret.add(new Node(x, y+1));

                            if(x <map.length-1 && map[y][x+1] == 1)
                                            ret.add(new Node(x+1, y));

                            if(y !=0 && map[y-1][x] == 1)
                                            ret.add(new Node(x, y-1));

                            if(x !=0 && map[y][x-1] == 1)
                                            ret.add(new Node(x-1, y));

                            return ret;
            }

            public static void main(String [] args){
                            WorldGenerator gen = new WorldGenerator();

            int ammountOfBlocks =200;
            int width = 25;
            int length = 25;
            int startX = 1;
            int startY = 1;
            int endX = 24;
            int endY = 24;
            int[][] map = gen.createWorld(ammountOfBlocks,width,length,startX,startY,endX,endY);
            int a=map.length;
            int b=map[0].length;
            int[][] map2=new int[b][a];
            for(int i=0; i<map.length; i++){
                for(int j=0; j<map[0].length;j++)
                {map2[j][i]=map[i][j];
                }
            }
                            PathFinder pf = new PathFinder(map,endX,endY);


                           /* for(int i = 0; i < map.length; i++){
                                            for(int j = 0; j < map[0].length; j++)
                                                            System.out.print(map[i][j] + " ");
                                            System.out.println();
                            }*/

                            long begin = System.currentTimeMillis();

                            List<Node> nodes = pf.compute(new PathFinder.Node(startX,startY));

                            long end = System.currentTimeMillis();


                            System.out.println("Time = " + (end - begin) + " ms" );
                            //System.out.println("Expanded = " + pf.getExpandedCounter());
                            System.out.println("Cost = " + pf.getCost());

                            if(nodes == null)
                                            System.out.println("No path");
                            else{

                                            for(int i=0; i<nodes.size();i++){
                                                            Node n=nodes.get(i);
                                                            int x= n.getX();
                                                            int y= n.getY();
                                                            map[x][y]=4;
                            }
                           /* for(int i = 0; i < map.length; i++){
                                            for(int j = 0; j < map[0].length; j++)
                                                            System.out.print(map[i][j] + " ");
                                            System.out.println();
                            }*/

            }

            }

}

WorldGenerator クラスは、1 と 0 の 2 次元配列のみを生成します。前もって感謝します!

4

1 に答える 1

0

ここで腰から撮影しますが、ヒューリスティックとして「直線距離」を使用したい場合は、コードにバグがあります。

現在のヒューリスティックは次のとおりです。デルタ x と y の合計が最小のノードが最も近いです。

5 x 5 のグリッドがあり、ターゲットが 2,2 にあるとしましょう。このヒューリスティックな 2,0 を使用すると、1,1 と同等に最適になりますが、これはもちろん間違っています。

新しいヒューリスティックにピタゴラスを使用してみてください。端までの距離が最も短いノードが最も近いということです。

protected Double h(Node from, Node to) {
    int dx = Math.abs(endx - to.x);
    int dy = Math.abs(endy - to.y);
    return new Double(Math.sqrt(dx*dx) + (dy*dy));
}

これにより、アルゴリズムはA* の基準であるhttp://en.wikipedia.org/wiki/Admissible_heuristicを使用します: http://en.wikipedia.org/wiki/A_star#Admissibility_and_optimality

お役に立てれば。


私のために働く解決策:

AStar.java

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;

public abstract class AStar<T> {

    private class Path<T> implements Comparable {

        public T point;
        public Double f;
        public Double g;
        public Path<T> parent;

        public Path() {
            parent = null;
            point = null;
            g = f = 0.0;
        }

        public Path(Path<T> p) {
            this();
            parent = p;
            g = p.g;
            f = p.f;
        }

        @Override
        public int compareTo(Object o) {
            AStar.Path p = (AStar.Path) o;
            return (int) (f - p.f);
        }

        public T getPoint() {
            return point;
        }

        public void setPoint(T p) {
            point = p;
        }
    }

    protected abstract boolean isGoal(T node);

    protected abstract Double g(T from, T to);

    protected abstract Double h(T from, T to);

    protected abstract List<T> generateSuccessors(T node, T parent);
    private PriorityQueue<AStar.Path> paths;
    private HashMap<T, Double> mindists;
    private Double lastCost;
    private int expandedCounter;

    public int getExpandedCounter() {
        return expandedCounter;
    }

    public AStar() {
        paths = new PriorityQueue<>();
        mindists = new HashMap<>();
        expandedCounter = 0;
        lastCost = 0.0;
    }

    protected Double f(AStar.Path p, T from, T to) {
        Double g = g(from, to) + ((p.parent != null) ? p.parent.g : 0.0);
        Double h = h(from, to);

        p.g = g;
        p.f = g + h;

        return p.f;
    }

    private void expand(Path<T> path) {
        if (expandedCounter > 1000000) {
            return;
        }
        T p = path.getPoint();
        Double min = mindists.get(path.getPoint());


        if (min == null || min.doubleValue() > path.f.doubleValue()) {
            mindists.put(path.getPoint(), path.f);
        } else {
            return;
        }

        List<T> successors = generateSuccessors(p, path.parent != null ? path.parent.getPoint() : null);

        for (T t : successors) {
            AStar.Path newPath = new AStar.Path(path);
            newPath.setPoint(t);
            f(newPath, path.getPoint(), t);
            paths.offer(newPath);
        }

        expandedCounter++;
    }

    public Double getCost() {
        return lastCost;
    }

    public List<T> compute(T start) {
        try {
            AStar.Path root = new AStar.Path();
            root.setPoint(start);

            /*
             * Needed if the initial point has a cost.
             */
            f(root, start, start);

            expand(root);

            for (;;) {
                Path<T> p = paths.poll();

                if (p == null) {
                    lastCost = Double.MAX_VALUE;
                    return null;
                }

                T last = p.getPoint();

                lastCost = p.g;

                if (isGoal(last)) {
                    LinkedList<T> retPath = new LinkedList<T>();

                    for (Path<T> i = p; i != null; i = i.parent) {
                        retPath.addFirst(i.getPoint());
                    }

                    return retPath;
                }
                expand(p);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
} 

PathFinder.java

package playground;

import java.util.*;

public class PathFinder extends AStar<PathFinder.Node> {

    private int[][] map;
    private int endx;
    private int endy;

    public static class Node {

        public int x;
        public int y;

        Node(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public String toString() {
            return "(" + x + ", " + y + ") ";
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }
    }

    public PathFinder(int[][] map, int endx, int endy) {
        this.map = map;
        this.endx = endx;
        this.endy = endy;
    }

    protected boolean isGoal(Node node) {
        return (node.x == endx) && (node.y == endy);
    }

    protected Double g(Node from, Node to) {

        if (from.x == to.x && from.y == to.y) {

            //   System.out.println("To x1 " + to.x);
            //     System.out.println("To y1 " + to.y);
            return 0.0;
        }

        if (map[to.y][to.x] == 1) {
            //System.out.println("To x2 " + to.x);
            //  System.out.println("To y2 " + to.y);

            return 1.0;
        }

        return Double.MAX_VALUE;
    }

    protected Double h(Node from, Node to) {
        int dx = Math.abs(endx - to.x);
        int dy = Math.abs(endy - to.y);
        return new Double(Math.sqrt(dx * dx) + (dy * dy));
        //return new Double(Math.abs(endx - to.x) + Math.abs(endy - to.y));
    }

    @Override
    protected List<Node> generateSuccessors(Node node, Node parent) {
        List<Node> ret = new LinkedList<Node>();
        int x = node.x;
        int y = node.y;
        if (y < map[0].length - 1 && map[y + 1][x] == 1 && (parent == null || (parent != null && !(parent.x == x && parent.y == y + 1)))) {
            ret.add(new Node(x, y + 1));
        }

        if (x < map.length - 1 && map[y][x + 1] == 1 && (parent == null || (parent != null && !(parent.x == x + 1 && parent.y == y)))) {
            ret.add(new Node(x + 1, y));
        }

        if (y != 0 && map[y - 1][x] == 1 && (parent == null || (parent != null && !(parent.x == x && parent.y == y - 1)))) {
            ret.add(new Node(x, y - 1));
        }

        if (x != 0 && map[y][x - 1] == 1 && (parent == null || (parent != null && !(parent.x == x - 1 && parent.y == y)))) {
            ret.add(new Node(x - 1, y));
        }

        return ret;
    }

    public static void main(String[] args) {
        int ammountOfBlocks = 200;
        int width = 25;
        int length = 25;
        int startX = 1;
        int startY = 1;
        int endX = 24;
        int endY = 24;
        int[][] map = {
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1},
            {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1},
            {1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1},
            {0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}
        };
        int a = map.length;
        int b = map[0].length;
        int[][] map2 = new int[b][a];
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[0].length; j++) {
                map2[j][i] = map[i][j];
            }
        }
        PathFinder pf = new PathFinder(map, endX, endY);


        /*
         * for(int i = 0; i < map.length; i++){ for(int j = 0; j <
         * map[0].length; j++) System.out.print(map[i][j] + " ");
         * System.out.println(); }
         */

        long begin = System.currentTimeMillis();

        List<Node> nodes = pf.compute(new PathFinder.Node(startX, startY));

        long end = System.currentTimeMillis();


        System.out.println("Time = " + (end - begin) + " ms");
        //System.out.println("Expanded = " + pf.getExpandedCounter());
        System.out.println("Cost = " + pf.getCost());

        if (nodes == null) {
            System.out.println("No path");
        } else {

            for (int i = 0; i < nodes.size(); i++) {
                Node n = nodes.get(i);
                int x = n.getX();
                int y = n.getY();
                map[x][y] = 4;
            }

            for(int i = 0; i < map.length; i++){ 
                for(int j = 0; j < map[0].length; j++) 
                    System.out.print(map[i][j] + " ");

                System.out.println(); 
            }
        }
    }
}
于 2013-03-21T18:07:58.763 に答える