Java Maze кратчайший путь 2d int массив - PullRequest
5 голосов
/ 08 декабря 2011

Я сейчас застрял в проекте.Моя цель - использовать алгоритм Дейкстры.

Я понимаю, что начинаю с точки (0,0), смотрю на два узла рядом с начальной точкой, а затем перехожу к наименьшему и смотрю на окружающиеузлы.Мой лабиринт случайный, но чтобы его было легче запустить, скажем, следующий лабиринт следующий:

Я начну с (0,0) и хочу закончить с (9,9), цифры - это уровень ОПАСНОСТИи мы нацелены на самый безопасный путь, а не на самый короткий.

Мне нужен толчок, чтобы понять, как это настроить.Я знаю, что мне нужен список или путь, чтобы сохранить то, где я нахожусь и где я хочу посмотреть.но я не знаю, как это сделать в java.

import java.awt.Point;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


public class solver {

    /**
     * @param args
     */
    public static int[][]maze;
    public static int[][]openlist;
    public static int[][]closed;
    public static int[][]copy;
    public static int danger;
    public static int size=100;
    static int Max=9;
    static int Min=0;
    public static List path = new ArrayList();
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        maze = new int[size][size];
        openlist = new int[size][size];
        closed = new int[size][size];
        copy = new int[size][size];
        filler(maze);
        copy=dijstkra();
        printer(maze);
        //printer(copy);
        EDSfAO(maze,0,0);
        printer(openlist);
        printer(copy);
    }
    private static Boolean notwall(int i, int j){

        if((i>maze.length-1)||(j>maze.length-1)||(i<0)||(j<0))
        {return false;}

        return true;}
    private static int[][] dijstkra(){


        for(int i=0;i<maze.length;i++){
            for(int j=0;j<maze.length;j++){
                copy[i][j]=100000000;
            }}
        copy[0][0]=0;
        return copy;
        }

    private static void EDSfAO(int[][] maze,int i,int j){
        int min=100000000;
        int holdx = 0;  
        int holdy = 0;
        openlist[i][j]=1;
        danger=copy[i][j];
        if(i==maze.length-1&&j==maze.length-1){

            printer(copy);
            for(holdx=0;holdx<path.size();holdx++){

                System.out.print(path.get(holdx));

            }


        }
        else{
        if(notwall(i+1,j)&&openlist[i+1][j]!=1){
            copy[i+1][j]=danger+maze[i+1][j];
        } if(notwall(i,j+1)&&openlist[i][j+1]!=1){
            copy[i][j+1]=danger+maze[i][j+1];
        } if(notwall(i,j-1)&&openlist[i][j-1]!=1){
            copy[i][j-1]=danger+maze[i][j-1];
        } if(notwall(i-1,j)&&openlist[i-1][j]!=1){
            copy[i-1][j]=danger+maze[i-1][j];
        }
        for(int x=0;x<maze.length;x++){
            for(int y=0;y<maze.length;y++){

            if(openlist[x][y]!=1){

                if(min>copy[x][y]){
                min=copy[x][y];
                holdx=x;    
                holdy=y;
                }

            }


        }}
        moveToPosition(holdx,holdy);
        }
    }


    private static void moveToPosition(int x, int y) {

            openlist[x][y]=1;
            path.add("("+x+","+y+")");
            openlist[x][y]=1;
            EDSfAO(maze,x,y);
    }

private static void printer(int[][] maze) {
        // TODO Auto-generated method stub
    for(int i=0;i<maze.length;i++){
        for(int j=0;j<maze.length;j++){
        System.out.print("["+maze[i][j]+"]");                       
        }
        System.out.println();
        }

    }

public static void filler(int[][] maze){

        for(int i=0;i<maze.length;i++){

            for(int j=0;j<maze.length;j++){
            //If i=0 AND j=0 then maze[0][0]= 0(start) OR i= maze.length-1 AND j= maze.length-1 then maze[maze.length][maze.length]=0
            if((i==0 && j==0) || (i==maze.length-1 && j==maze.length-1)){

                maze[i][j]=0;   

            }else{
                maze[i][j]=Min + (int)(Math.random() * ((Max - Min) + 1));
            }
            }
            }
    }
}

Лабиринт соединен без стен, все коробки - комнаты.Я пытался поработать над этим какое-то время, и я действительно мог бы использовать толчок.Я смотрел много видео об алгоритме Диджстры. Я просто в данный момент действительно потерян.

Я добавил массив, в котором я сохраняю опасность, начиная с создания узла 100000000, но начальный узел (0,0)0.

МОЖЕТ ли кто-нибудь помочь мне со следующими шагами? Я понимаю, что это домашнее задание, но у меня заканчивается время, и мне действительно нужны некоторые указатели.

ОБНОВЛЕНИЕ:

ОК, поэтому яиметь это рабочее.Он печатает путь, по которому он идет, но если он находит лучший путь, он печатает оба, может кто-нибудь помочь мне исправить это.

Также он ломается, если я делаю элемент 100X100, может кто-то сказать мне, почему?Это последнее из настоящих «заданий по программированию».Как вы можете ожидать, это будет связано с графиками (с изюминкой);но, конечно, будет также какое-то решение проблем.Инструкция


Представьте себе «игру в темницу», в которой все комнаты расположены в идеальной сетке и в квадратной среде.В каждой комнате есть существо, представляющее некоторую степень опасности в диапазоне от 0 (безвредно) до 9 (избегание будет разумным).Цель состоит в том, чтобы найти путь через подземелье от начала до конца, который минимизирует общее количество опасности.

Каждая комната, за исключением границ, существует только в направлениях вверх, вниз, влево, вправо (нетдиагоналей).Вход слева вверху (0,0), а выход справа внизу (n-1, n-1).

Перечислите все «комнаты», которые необходимо пройти, вформа пути координат комнаты от начала до конца.

Например:

(0,0) (1,0) (2,0) (2,1)(2,2) (2,3) (3,3) (4,3) (4,4)

Общая опасность = 11 Ввод

Входной файл будет состоять из 100 строкиз 100 цифр, 0-9.(Да, 10 000 - это много комнат, но, к счастью, наш бесстрашный путешественник никогда не покидает дом без портативного компьютера и коллекции электронных наборов данных на все случаи жизни, полученных в прошлом году на праздничном обмене подарками; вероятно, он был одарен.) *

* Для этого задания вы должны будете сгенерировать свои собственные тестовые данные.Вот почему я не хожу на вечеринки такого рода ... Вывод

Программа должна записать вывод в файл (в формате, показанном выше, включая вывод "Общая опасность").Спасибо.

ОБНОВЛЕНИЕ2: я обнаружил ошибку в своем кодировании У меня есть

if(min>copy[x][y]){
                min=copy[x][y];
                holdx=x;    
                holdy=y;
                }

Это позволит проверить каждый путь, который находится в данной точке, мой кратчайший путь больше, чем другойпуть, как мне это исправить?

Что мне не хватает?ОБНОВЛЕНИЕ Я закончил это спасибо за ОЧЕНЬ маленькую помощь.

Ответы [ 4 ]

11 голосов
/ 08 декабря 2011

Как только вы поймете, как использовать алгоритм Дейкстры, вы можете использовать ArrayList, содержащий пары чисел, обозначающих ваши предыдущие позиции:

List<Point> path = new ArrayList<Point>();

Затем вы можете написать класс Point, который просто переноситдва int примитива, x и y.

Поэтому, когда вы двигаетесь, вы можете использовать:

private void moveToPosition(int x, int y) {
    path.add(new Point(x, y));

    // Move yourself to this point
    ...
}

Что касается обучения тому, как на самом деле использовать Алгоритм, я думаю, что это в какой-то степени ваша домашняя работа, и мы не хотим портить вам веселье!

Статья об алгоритме в Википедии довольно полезна, хотя, думаю, ваши заметкитакже помогите.

Алгоритм Дейкстры в Википедии

4 голосов
/ 16 декабря 2011

Вы можете основывать свое решение на алгоритмах, найденных в «Введение в алгоритмы» Кормена, Лизерсона, Ривеста и Стейна, 2-е издание. В главе 24 они анализируют алгоритм «кратчайшие пути из одного источника», а в 24.3 у вас есть Дейкстра.

Я буду использовать псевдокод здесь. Вы можете заменить приведенную ниже очередь с минимальным приоритетом другой структурой данных, такой как карта в Java. Это не будет быстро, но это будет работать, и это может быть удовлетворительной первой попыткой. У меня также есть Java-реализация очереди с минимальным приоритетом, если хотите.

Скажем, у вас есть лабиринт, представленный двумерным массивом, как в вашем коде: int [M] [N] лабиринт. Первый индекс - это индекс строки, а второй - индекс столбца, начиная с нуля. Таким образом происходит от 0 до M-1 для строк и от 0 до N-1 для столбцов. Значение лабиринт [строка] [столбец] представляет опасность, связанную с комнатой в (строка, столбец). Нам нужно удобное представление, чтобы определить вес между двумя комнатами в лабиринте и узнать, какие комнаты соседствуют с конкретной комнатой.

Идея состоит в том, чтобы сгладить массив и поместить каждую строку рядом: row1, row2, ..., rowM. Тогда мы можем дать индекс i для каждой комнаты. Чтобы использовать эту идею, нам нужно преобразовать координаты разных типов: (строка, столбец) -> i и i -> (строка, столбец).

convert_to_index(row, column) ::= row * N + column
convert_to_pair(i) ::= (i div N, i modulo N)

Скажите РАЗМЕР M * N, общее количество комнат в лабиринте.

Теперь мы можем создать матрицу смежности, представляющую лабиринт со значениями опасности: int [SIZE] [SIZE] adjacency_matrix, первый индекс - FROM, второй - TO. В клетке мы находим стоимость или вес, чтобы перейти из одной комнаты в другую. Обратите внимание, что с учетом конкретной комнаты, есть только несколько комнат, которые примыкают к этой комнате. Другие комнаты в лабиринте не доступны из этой комнаты. По соглашению мы будем использовать самое большое целое число, чтобы указать это, и использовать константу INFINITY. Другие значения представляют опасность и находятся в диапазоне от 0 до 9. Матрица будет разреженной, и существуют методы для ее оптимизации.

Когда у нас есть комната, расположенная в (r, c), какие комнаты рядом с ней? Мы хотим, чтобы вектор индексов использовался непосредственно в нашем алгоритме. Если мы не принимаем во внимание границы лабиринта, мы имеем: (r-1, c-1), (r-1, c), (r-1, c + 1), (r, c-1) , (r, c + 1), (r + 1, c-1), (r + 1, c) и (r + 1, c + 1). Затем мы можем применить convert_to_index к каждому из них, проверить, что они находятся в диапазоне 0..SIZE-1, и инициализировать матрицу с этим. Таким образом, например, переход от (r, c) к (r-1, c-1) имеет стоимость или опасность лабиринта [r-1, c-1] и переход от (r-1, c-1) к (r, c) имеет стоимость лабиринта [r, c]. Но переход из (r, c) в другую отдаленную комнату стоит 10, он недоступен, и обратное верно.

adjacent_rooms(r, c) ::=
    Given the vector [(r-1, c-1), (r-1, c), (r-1, c+1), (r, c-1), (r, c+1), (r+1, c-1), (r+1, c), (r+1,c+1)]
    Filter it by deleting pairs whose row is not in 0..M-1 or whose column is not in 0..N-1
    Then apply the function convert_to_index to each resulting pair (map operation)
    Return the result

for i in 0..SIZE-1 loop
    for j in 0..SIZE-1 loop
        adjacency_matrix[i, j] := -1
    end loop
end loop

for i in 0..SIZE-1 loop
    (current_r, current_c) := convert_to_pair(i)
    adjacent_room_indexes := adjacent_rooms(current_r, current_c)
    for j in 0..SIZE-1 loop
        if adjacency_matrix[i, j] == -1 then
            (r, c) := convert_to_pair(j)
            if i == j then adjacency_matrix[i, j] := 0
            elsif j in adjacent_room_indexes then adjacency_matrix[i, j] := maze[r, c]; adjacency_matrix[j, i] := maze[current_r, current_c]
            else adjacency_matrix[i, j] := INFINITY
        end if
    end loop
end loop

Далее нам нужны векторные оценки кратчайших путей (см. Стр. 585 книги) и вектор предшественников (см. Стр. 584 книги).

for i in 0..SIZE-1 loop
    predecessors[i] := NONE
    estimates[i] := INFINITY
end loop

Переход от начала к началу стоит 0.

estimates[0] := 0

Инициализировать набор вершин, принадлежащих MST (минимальное связующее дерево)

mst := empty set

Инициализирована очередь с минимальным приоритетом q

for i in 0..SIZE-1 loop
    q.add(i, estimates[i])
end loop

until q.empty? loop
    u, u_d = q.delete_min
    mst.add(u)
    (current_r, current_c) := convert_to_pair(i)
    adjacent_room_indexes := adjacent_rooms(current_r, current_c)
    for i in 0..adjacent_room_indexes.length-1 loop
        v := adjacent_room_indexes[i]
        cost = adjacency_matrix[u, v]
        if cost < q[v]
          predecessors[v] = u
          estimates[v] = c
          q[v] = c
        end
    end loop
end loop

Работа выполнена. У нас наш путь в predecessors с затратами в estimates.

Это может быть излишним, а A * может быть лучше. Но я думаю, что использование Dijkstra было требованием вашей домашней работы. Если вы хотите изучить A *, я предлагаю вам взглянуть на A * Pathfinding для начинающих и Amit's A * Pages .

4 голосов
/ 08 декабря 2011

Я только что посмотрел статью в Википедии о Алгоритм Дейкстры .

  1. Назначьте каждому узлу предварительное значение расстояния: установите его равным нулю для нашего начального узла и бесконечности для всех других узлов.
  2. Пометить все узлы как непосещенные. Установите начальный узел как текущий. Создайте набор из непосещенных узлов, который называется непосещенным множеством. состоящий из всех узлов, кроме начального узла.
  3. Для текущего узла рассмотрите все его не посещенные соседи и вычислите их предварительные расстояния. Например, если текущий узел А обозначен расстоянием 6, а край, соединяющий его с сосед B имеет длину 2, тогда расстояние до B (через A) будет 6 + 2 = 8. Если это расстояние меньше ранее записанного расстояния, затем переписать это расстояние. Хотя сосед был осмотрен, он не помечен как посещенный в настоящее время, и он остается в непосещенный набор.
  4. Когда мы закончим с учетом всех соседей текущего узла, отметьте текущий узел как посещенный и удалите его из непосещенный набор. Посещенный узел никогда не будет проверен снова; его записанное расстояние является окончательным и минимальным.
  5. Если не посещенный набор пуст, остановитесь. Алгоритм завершен.
  6. Установите не посещаемый узел, помеченный наименьшим предварительным расстоянием, как следующий «текущий узел» и вернитесь к шагу 3.

Просто наивно подойди к этому. Черт возьми, относись к этому как к «пониманию чтения программистом». В этом случае хитрость заключается в отображении вашего двумерного массива в набор узлов графа. Каждому узлу требуется «предварительное значение расстояния». Хорошо, ваши узлы определяются по их значению i, j. Сделайте что-нибудь, чтобы вы могли получить / установить значение tenual_distance_value с учетом i и j.

Вам необходимо пометить посещение узла. Та же идея.

Вам нужен «текущий узел». Та же идея, но проще.

Я знаю, что мне нужен список или путь, по которому можно было остаться. Я и где я хочу смотреть. но я не знаю, как это сделать в Java.

Технически, вам не нужно поддерживать путь для запуска алгоритма. Вам придется выяснить, как построить его из результатов алгоритма, если вы этого не сделаете, но это, безусловно, возможно.

2 голосов
/ 13 декабря 2011

Я реализовал алгоритм, упомянутый ccoakley. Ниже приведен частичный код, который поможет вам в правильном направлении:

<code>import java.util.HashSet;
import java.util.Set;

// 1. Assign to every node a tentative distance value: set it to zero for our initial node and to infinity for all other nodes.
// 2. Mark all nodes unvisited. Set the initial node as current. Create a set of the unvisited nodes called the unvisited set consisting of all the nodes except the initial node.
// 3. For the current node, consider all of its unvisited neighbors and calculate their tentative distances. For example, if the current node A is marked with a distance of 6, and the edge connecting it with a neighbor B has length 2, then the distance to B (through A) will be 6+2=8. If this distance is less than the previously recorded distance, then overwrite that distance. Even though a neighbor has been examined, it is not marked as visited at this time, and it remains in the unvisited set.
// 4. When we are done considering all of the neighbors of the current node, mark the current node as visited and remove it from the unvisited set. A visited node will never be checked again; its distance recorded now is final and minimal.
// 5. If the unvisited set is empty, then stop. The algorithm has finished.
// 6. Set the unvisited node marked with the smallest tentative distance as the next "current node" and go back to step 3.
public class Dijkstra {

    class Node {
        String name;
        Integer distance = Integer.MAX_VALUE;
        boolean visited;
        Set<Edge> edges = new HashSet<Edge>();

        Node(String name) {
            this.name = name;
        }

        Edge connect(Node destination, int length) {
            Edge edge = new Edge();
            edge.length = length;
            edge.from = this;
            edge.to = destination;
            edges.add(edge);
            destination.edges.add(edge);
            return edge;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    class Edge {
        int length;
        Node from;
        Node to;

        Node getNeighbor(Node origin) {
            if (from == origin) {
                return to;
            }
            else if (to == origin) {
                return from;
            }
            else {
                throw new IllegalArgumentException("This edge is not connected to node " + origin);
            }

        }

        @Override
        public String toString() {
            return String.format("%s-%s", from, to);
        }
    }

    /**
     * <pre>
     * a - b - c
     * |   |   
     * d - e   |
     * |
     * f - g - h
     * 
* * @вернуть * / приватный набор initialize () { Узел а = новый узел ("а"); Узел b = новый узел ("b"); Узел c = новый узел ("c"); Узел d = новый узел ("d"); Узел e = новый узел ("e"); Узел f = новый узел ("f"); Узел g = новый узел ("g"); Узел h = новый узел ("h"); соединение (б, 4); соединение (д, 8); b.connect (c, 6); b.connect (e, 1); c.connect (ч, 7); d.connect (e, 2); d.connect (f, 5); f.connect (g, 3); g.connect (h, 1); a.distance = 0; Set unvisited = new HashSet (); unvisited.add (а); unvisited.add (б); unvisited.add (с); unvisited.add (д); unvisited.add (е); unvisited.add (е); unvisited.add (г); unvisited.add (ч); вернуть не побывавшим; } приватный набор решить (набор не посещен) { Set посещения = новый HashSet (); while (! unvisited.isEmpty ()) { System.out.println (String.format ("Не посещенные узлы:% s", unvisited.size ())); печать (непосещенная); Node current = findNodeWithSmallestDistance (не посещено); System.out.println (String.format («Текущий узел:% s», текущий)); updateNeighbors (тока); current.visited = true; unvisited.remove (ток); visited.add (ток); } вернуться посетил; } private void updateNeighbors (текущий узел) { для (Edge edge: current.edges) { Узел сосед = edge.getNeighbor (текущий); if (! neighbour.visited) { int tenlativeNeighborDistance = current.distance + edge.length; if (tenlativeNeighborDistance узлов) { Узел самый маленький = ноль; for (Узел узла: узлы) { if (smalllest == null || node.distance посещен) { for (Узел узла: посещенный) { System.out.println (String.format («Узел:% s имеет расстояние:% s», узел, node.distance)); } } public static void main (String [] args) { Dijkstra edsger = новый Dijkstra (); Set unvisited = edsger.initialize (); Установите visit = edsger.solve (не посещено); edsger.print (посетили); } }

РЕДАКТИРОВАТЬ: добавлены недостающие биты

...