Имея список с лабиринтами домов (2 измерения), как мне создать ориентированный граф со словарем - PullRequest
0 голосов
/ 19 января 2011

Я могу сделать только ненаправленный график.без понятия о том, как я могу сделать направленный.

Есть идеи?

Ответы [ 3 ]

1 голос
/ 20 января 2011

Извинения за довольно длинный переулок.У меня было время убить в поезде.

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

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

Подход

Один простой способ сделать это - сначала сохранить карту в более удобном для навигации формате.затем, начиная с начального узла, выполните следующие действия:

  1. ищите соседей (сверху, снизу, слева, справа)
  2. для каждого соседа:
    • еслиэто не возможный путь, игнорируйте
    • , если мы обрабатывали этот узел раньше, игнорируем
    • , добавьте этот узел как ребро и поместите его в очередь (не в стек, подробнее об этомпозже) для дальнейшей обработки
  3. для каждого узла в очереди / стеке повторите процедуру с шага 1.

(см. пример реализации ниже)

На этом этапе вы получите направленный ациклический граф (DAG) с начальным узлом в верхней части дерева и конечным узлом в качестве одного из листьев.Решить это было бы легко на этом этапе.См. этот ответ по решению лабиринта, представляющего собой граф .

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

стек или очередь?

Обратите внимание, что использование стека (первый в последнем выходе) будет означать построение графика способом глубина-сначала при использовании очереди (первый-первый-первый) приведет к подходу в ширину .

Как правило, вы хотите использовать очередь (шириной в первую очередь, если вы хотите найти кратчайший путь. Рассмотрите следующую карту:

       START
########   ###### 
########   ######
### b    a ######  
###   ##   ######
###   ## e ######
### c    d ######
########   ######
########      END
#################

Если путь проходит сначала в глубинуи в ответвлении a вы выбираете путь a->b до a->e, в результате вы получаете график:

 START
   |
   a
  / \
 b   e   <-- end, since d already visited
 |
 c
 |
 d
  \
   END

Однако при использовании подхода в ширину получится путь a->eчерез узел d ранее, что привело к более короткому графику и лучшему решению:

 START
   |
   a
  / \
 b   e 
 |   |
 c   d
     |
    END

Пример кода

Пример ввода при условии:

..........
#########.
..........
.#########
......#...
#####...#.
##...####.
##.#......
...#######

e = (0,0)
s = (8,0)

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Следующий код написан для ясности, а не для скорости. Он не полностью протестирован, поэтому нет гарантии правильности, но он должен дать вам представление о том, что возможно.

Мы предполагаем, чтовходной файл отформатирован согласованно. Большинство проверок на ошибки оставлено для краткости.

# regex to extract start/end positions
import re
re_sepos = re.compile("""
    ^([se])\s* # capture first char (s or e) followed by arbitrary spaces
    =\s*        # equal sign followed by arbitrary spaces
    \(          # left parenthesis
    (\d+),(\d+) # capture 2 sets of integers separated by comma
    \)          # right parenthesis
""", re.VERBOSE)

def read_maze(filename):
    """
    Reads input from file and returns tuple (maze, start, end)
      maze : dict holding value of each maze cell { (x1,y1):'#', ... }
      start: start node coordinage (x1,y1)
      end  : end node coordinate (x2,y2)
    """
    # read whole file into a list
    f = open(filename, "r")
    data = f.readlines()
    f.close()

    # parse start and end positions from last 2 lines
    pos = {}
    for line in data[-2:]: 
        match = re_sepos.match(line)
        if not match:
            raise ValueError("invalid input file")
        c,x,y = match.groups() # extract values
        pos[c] = (int(x),int(y))
    try:
        start = pos["s"]
        end   = pos["e"]
    except KeyError:
        raise ValueError("invalid input file")

    # read ascii maze, '#' for wall '.' for empty slor 
    # store as maze = { (x1,y1):'#', (x2,y2):'.', ... }
    # NOTE: this is of course not optimal, but leads to a simpler access later 
    maze = {}
    for line_num, line in enumerate(data[:-3]): # ignore last 3 lines
        for col_num, value in enumerate(line[:-1]): # ignore \n at end
            maze[(line_num, col_num)] = value

    return maze, start, end

def maze_to_dag(maze, start, end):
    """
    Traverses the map starting from start coordinate.
    Returns directed acyclic graph in the form {(x,y):[(x1,y1),(x2,y2)], ...}
    """
    dag = {}        # directed acyclic graph
    queue = [start] # queue of nodes to process

    # repeat till queue is empty
    while queue:
        x,y = queue.pop(0)      # get next node in queue
        edges = dag[(x,y)] = [] # init list to store edges

        # for each neighbour (top, bottom, left, right)
        for coord in ((x,y-1), (x,y+1), (x-1,y), (x+1,y)): 
            if coord in dag.keys(): continue   # visited before, ignore

            node_value = maze.get(coord, None) # Return None if outside maze
            if node_value == ".": # valid path found
                edges.append(coord) # add as edge
                queue.append(coord) # push into queue

                # uncomment this to stop once we've found the end point
                #if coord == end: return dag

    return dag

if __name__ == "__main__":
    maze,start,end = read_maze("l4.txt")
    dag = maze_to_dag(maze, start, end)
    print dag
0 голосов
/ 19 января 2011

Поскольку у вас уже есть список, попробуйте создать Матрицу смежности вместо словаря.

list_of_houses = []
directed_graph = [][]
for i in xrange(len(list_of_houses)):
    for i in xrange(len(list_of_houses)):
        directed_graph[i][i] = 0

Тогда для любого нового ребра от одного дома к другому (или с соединением)

directed_graph[from_house][to_house] = 1

и все готово. Если есть ребро от house_a до house_b, тогда directed_graph[house_a][house_b] == 1.

0 голосов
/ 19 января 2011

Эта страница предоставляет хороший учебник по реализации графов с помощью Python.Из статьи приведен пример графа каталогов, представленного словарем:

graph = {'A': ['B', 'C'],
         'B': ['C', 'D'],
         'C': ['D'],
         'D': ['C'],
         'E': ['F'],
         'F': ['C']}

Тем не менее, вы также можете посмотреть существующие библиотеки графов, такие как NetworkX и * 1008.* igraph .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...