Java:优化/提高DFS中的运行速度

时间:2012-04-16 16:32:12

标签: java arraylist depth-first-search

所以我一直在研究一个问题,基本前提是给定一个任意大小的网格,我需要计算“游览”的数量。游览是从左上角开始的运行(我使用点x = 1,y = 1表示)并且在左下角结束(x = 1 y = max,无论'y'的最大值是)。除此之外,它必须触及沿途的每个其他点,并且只能访问网格中的任何一点。

我在下面写的方式,它在~42-45秒内运行,但如果可能的话,我想让它在30秒或更短的时间内运行。那么,我的问题是,我可以改变或取出哪些能使它更快地运行?

我已经尝试将所有内容都设置为静态并实例化该类(这为我的运行时间增加了几秒)。

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.Date;

public class CodingPuzzle
{
public static List<Point> lAllPoints = new ArrayList<Point>();
public static int iMaxX;
public static int iMaxY;
public static int iCompletePaths = 0;

public static void depthFirstSearch(Point current, Stack<Point> result)
{
    if (result.contains(current))
        return;

    result.push(current);

    if (current.x == 1 && current.y == iMaxY && result.size() == iMaxX*iMaxY)
    {
        // This is a complete path
        iCompletePaths++;
    }
    for (Point p: getPossibleMoves(current))
    {
        depthFirstSearch(p, result);
    }

    // No path was found
    result.pop();
    return;
}

public static List<Point> getPossibleMoves (Point fromPoint)
{
    int iCurrentPointIndex = lAllPoints.indexOf(fromPoint);
    List<Point> lPossibleMoves = new ArrayList<Point>();

    if (fromPoint.x == 1 && fromPoint.y == 1)
    {
        // Top left point
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + iMaxY));
    }

    else if (fromPoint.x == 1 && fromPoint.y == iMaxY)
    {
        // Bottom left point. Should always be the last point. No valid moves.
        // If a path gets here before the end it shouldn't need to continue.
    }

    else if (fromPoint.x == iMaxX && fromPoint.y == 1)
    {
        // Top right point
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
    }

    else if (fromPoint.x == iMaxX && fromPoint.y == iMaxY)
    {
        // Bottom right point
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - 1));
    }

    else if (fromPoint.x == 1 && fromPoint.y != iMaxY)
    {
        // Any other point on the left side
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + iMaxY));
    }

    else if (fromPoint.x == iMaxX)
    {
        // Any other point on the right side
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
    }

    else if (fromPoint.y == 1)
    {
        // Any other point on the top
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + iMaxY));
    }

    else if (fromPoint.y == iMaxY && fromPoint.x != 1)
    {
        // Any other point on the bottom
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + iMaxY));
    }

    else
    {
        // Any other point not on an edge.
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - 1));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex - iMaxY));
        lPossibleMoves.add(lAllPoints.get(iCurrentPointIndex + iMaxY));
    }

    return lPossibleMoves;
}

public static void setUpGrid(int x, int y)
{
    iMaxX = x;
    iMaxY = y;
    for (int i = 1; i <= x; i++)
        for (int j = 1; j <= y; j++)
            lAllPoints.add(new Point(i, j));
}

public static void main(String[] args)
{
    Date start = new Date(System.currentTimeMillis());
    setUpGrid(10, 4);
    Stack<Point> sCurrentPoints = new Stack<Point>();
    depthFirstSearch(lAllPoints.get(0), sCurrentPoints);
    Date end = new Date(System.currentTimeMillis());
    long total = end.getTime() - start.getTime();
    System.out.println(iCompletePaths + " paths found in " + total/1000 + " seconds.");
}

3 个答案:

答案 0 :(得分:3)

我会减少代码为完成计数所做的工作量。

public class CodingPuzzle2 {
    private final int width;
    private final int height;

    public CodingPuzzle2(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int countPaths(int x, int y) {
        boolean[][] visited = new boolean[width][height];
        int[] count = {0};
        countPaths(x, y, visited, 1,  count);
        return count[0];
    }

    private void countPaths(int x, int y, boolean[][] visited, int depth, int[] count) {
        if (x < 0 || x >= width || y < 0 || y >= height || visited[x][y])
            return;
        visited[x][y] = true;
        try {
            if (x == 0 && y == height - 1) {
                if (depth == width * height)
                    count[0]++;
                return;
            }
            countPaths(x, y + 1, visited, depth+1, count);
            countPaths(x + 1, y, visited, depth+1, count);
            countPaths(x - 1, y, visited, depth+1, count);
            countPaths(x, y - 1, visited, depth+1, count);
        } finally {
            visited[x][y] = false;
        }
    }

    public static void main(String... args) {
        long start = System.nanoTime();
        CodingPuzzle2 cp = new CodingPuzzle2(10,4);
        int count = cp.countPaths(0, 0);
        long time = System.nanoTime() - start;
        System.out.printf("%,d paths found in %.3f seconds.%n", count, time / 1e9);
    }
}

打印

2,329 paths found in 1.758 seconds.

答案 1 :(得分:0)

我要做的第一件事是描述你的代码(正如彼得在你的问题的评论中所说)。如果不知道你在哪里花时间,就很难“让事情变得更快”。但是你的算法可能效率低下。对于分析,您可以使用随JVM一起提供的jvisualvm。

您可能希望查看algorithms来解决图表的Hamiltonian Path,这实际上就是您正在做的事情。有比蛮力更有效的解决方案。

答案 2 :(得分:0)

别人怎么说。它将帮助您成为更好的程序员。

除此之外,这是您正在寻找的快速胜利 - 从Stack切换到Deque,即ArrayDeque。这一次更改使我的Java 7 -server计算机上的速度提高了44%。