Хобрук: Ваш путь к мастерству в программировании

Алгоритм: поиск пути с переменной шириной пути

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

Путь будет представлен двумерным массивом, где 0 означает, что по нему нельзя пройти, 1 означает, что по нему можно пройти, 2 представляет собой начальную точку, а 3 представляет собой конечную точку. Рассмотрим следующий пример:

21111111100000
00000011000000
00001111111111
00001111100111
00001110000101
00001111100113

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

Отредактировано: путь не должен содержать ненужную «пустую» прогулку, что означает, что если вертикальный путь имеет ширину 2, результат не должен просто идти вниз по пути, а затем делать один шаг вправо, а затем идти до конца.

20.02.2015

  • Подойдет любой алгоритм поиска кратчайшего пути, Дейкстрас, Беллман-форд. Каковы предпочтения по алгоритму, скорости? 20.02.2015
  • @Calum легко реализовать, это главный приоритет. Ширина и высота сетки должны быть меньше 1000x1000, поэтому я не думаю, что скорость будет проблемой. Я думаю, что некоторые алгоритмы пути зацикливаются или выбирают ненужные пути, но я могу ошибаться. 20.02.2015
  • Я бы предложил использовать алгоритм поиска в глубину и записывать, где вы уже были (возможно, использовать координаты), чтобы вы не сталкивались с петлями. 20.02.2015
  • @Calum в приведенном примере некоторые алгоритмы будут создавать путь, который будет проходить весь путь вправо, затем вниз, влево, вниз, вверх, вправо и затем вниз, обратите внимание, что, поскольку ширина некоторых путей равна 1, поэтому dfs будет скажи нет петли. 20.02.2015
  • это будет один путь, но большинство алгоритмов поиска в глубину, достигнув тупика, вернутся назад и попробуют другой маршрут. Вы должны следить за тем, где вы были 20.02.2015
  • Я предлагаю найти уже существующий модуль, который вы можете использовать, а не писать алгоритм самостоятельно, если простота является вашим критерием. 20.02.2015
  • @ Калум, хорошо, что это не тупик, когда он достигает дна, он может либо пройти весь путь вправо, либо вправо на один шаг, а затем полностью вверх. поскольку ни один из узлов не был посещен, это не цикл. Теперь это путь, который я не хочу. 20.02.2015
  • правда, но, отслеживая, где вы уже были, вы перестанете зацикливаться, т. е. если я знаю, что уже был на позиции, и я окажусь там снова, я знаю, что сделал петлю и должен вернуться назад 20.02.2015
  • @ Калум, да, я понимаю, что ты имеешь в виду. Но в примере, который я только что привел, хотя это и не прямая петля, она по-прежнему считается для меня петлей, поскольку она шла по пути, а затем возвращалась обратно. Я не хочу никакой петли. Особенно, когда есть возможность находиться на открытой местности, не хочется с небольшим шансом ходить по всей местности. 20.02.2015
  • Имеет ли для вас значение ширина? То есть вам нужно найти весь широкий путь или только одну нить из соединенных точек? 20.02.2015
  • @Calum: BFS намного лучше, поскольку он найдет вам кратчайший путь за разумное время. Еще лучше использовать BFS с обоих концов, пока оба поиска не достигнут общей точки; в сетке MxN вы можете гарантировать, что будет затронуто не более MN/4 точек, даже если все точки помечены как проходимые. 20.02.2015
  • @rici нет, ширина пути не имеет значения. Все, что мне нужно, это одна цепочка соединенных точек, которая не содержит прямой или косвенной петли. 20.02.2015
  • @ Стив, ты продолжаешь использовать слово «петля», но то, что ты описываешь, не является петлей. Это обратный путь, путь, который возвращается туда, где он был раньше (но не точно). Чтобы образовалась петля, она должна пройти точно в одно и то же место. Все пути трека имеют свойство, позволяющее сократить часть трекбека. Поэтому верный способ гарантировать отсутствие трекбэков — использовать алгоритм кратчайшего пути. 20.02.2015
  • @rici BFS дает более качественные результаты, но, возможно, его сложнее написать. Исходный вопрос задавался для любого пути (не обязательно кратчайшего) с использованием простейшего алгоритма. Конечно, все по-другому, если вы хотите принять во внимание добавленное требование Стива о пути без обратного пути. 20.02.2015
  • @redtuna: Единственная разница между BFS и DFS заключается в том, что в DFS вы используете рекурсию, а в BFS — очередь. Для фактического алгоритма кратчайшего пути вам нужна карта от узла к предыдущему узлу, которая позволит вам восстановить путь, когда вы достигнете цели. (В языке с реальными списками CONS вы просто переворачиваете список, когда попадаете в цель, что будет точно таким же, как DFS.) Так что я бы сказал, что разница в сложности тривиальна, и OP было бы хорошо - обслуживается преодолением его. 20.02.2015
  • @ричи да. Разница в том, что я пытался ответить на вопрос ОП, а вы пытаетесь подтолкнуть их к изучению новых и полезных вещей. 20.02.2015
  • @redtuna: Моей первой мыслью было: «Конечно, виновен в предъявленном обвинении». Дайте человеку рыбу.... Но, действительно, нет никаких оснований предполагать, что BFS - это что-то новое, а DFS - нет. ОП в любом случае хочет узнать что-то новое, так что они могли бы также изучить лучший вариант для этой проблемы. Но чтобы было понятнее мои мотивы, я пытаюсь подтолкнуть не только ОП к изучению новых и полезных вещей. 21.02.2015

Ответы:


1

Я согласен с Калумном: DFS — самый простой подход. Вот простое решение в псевдокоде, похожем на python. Он напечатает решение в виде последовательности «L», «R», «U», «D», чтобы указать «влево», «вправо», «вверх» или «вниз».

def flood(x,y,story):
  if (visited[x][y] or map[x][y]=='0'): return;
  visited[x][y]=True;
  if (map[x][y]=='3'): 
    print 'done. The path is: '+story
    return
  if (x<len(a[0])): flood(x+1,y,story+'R')
  if (y<len(a)): flood(x,y+1,story+'D')
  if (x>0): flood(x-1,y,story+'L')
  if (y>0): flood(x,y-1,story+'U')

def solve(map):
  visited = array_of_false_of_same_size_as(map)
  x,y = find_the_two(map)
  flood(x,y,'')

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

(p.s. Я сделал этот ответ вики-сообществом, так как я просто поясняю ответ Калумна. Я не могу претендовать на большую заслугу)

Версия поиска в ширину, также на Python

Для чего бы это ни стоило, и просто чтобы показать, что поиск в ширину не так уж и сложен, реальная исполняемая программа на Python:

def find(grid, xstart=0, ystart=0):
  # Maps (xi,yi) to (x(i-1), y(i-1))
  prev = {(xstart, ystart):None}

  # Prepare for the breadth-first search
  queue = [(xstart, ystart)]
  qpos = 0
  # Possibly enqueue a trial coordinate
  def enqueue(prevxy, dx, dy):
    x = prevxy[0] + dx
    y = prevxy[1] + dy
    xy = (x, y)
    # Check that it hasn't been visited and the coordinates
    # are valid and the grid position is not a 0
    if (xy not in prev
        and x >= 0 and x < len(grid)
        and y >= 0 and y < len(grid[x])
        and grid[x][y] != 0):
      # Record the history (and the fact that we've been here)
      prev[xy] = prevxy
      # If we found the target, signal success
      if grid[x][y] == 3:
        return xy
      # Otherwise, queue the new coordinates
      else:
        queue.append(xy)
        return None

  # The actual breadth-first search
  while qpos < len(queue):
    xy = queue[qpos]
    qpos += 1
    found = (  enqueue(xy, 1, 0)
            or enqueue(xy, 0, 1)
            or enqueue(xy, -1, 0)
            or enqueue(xy, 0, -1))
    if found: break

  # Recover the path
  path = []
  while found:
    path.append(found)
    found = prev[found]
  path.reverse()
  return path

# Test run    
grid = [ [2,1,1,1,1,1,1,1,1,0,0,0,0,0]
       , [0,0,0,0,0,0,1,1,0,0,0,0,0,0]
       , [0,0,0,0,1,1,1,1,1,1,1,1,1,1]
       , [0,0,0,0,1,1,1,1,1,0,0,1,1,1]
       , [0,0,0,0,1,1,1,0,0,0,0,1,0,1]
       , [0,0,0,0,1,1,1,1,1,0,0,1,1,3]
       ]
for x, y in find(grid): grid[x][y]='*'
print '\n'.join(''.join(str(p) for p in line) for line in grid)

Выход:

*******1100000
000000*1000000
000011******11
00001111100*11
00001110000*01
00001111100***
20.02.2015
  • это решение избегает цикла? Например, если путь прямо вниз с шириной 2, будет ли этот алгоритм сначала проходить весь путь вниз, а затем идти вправо на один шаг, а затем полностью вверх? 20.02.2015
  • Это решение, да. Если есть путь от 2 до 3, он его найдет. Is использует посещаемую структуру данных, чтобы избежать бесконечных циклов. 20.02.2015
  • Этот алгоритм также не будет отслеживаться в приведенном вами примере, хотя в исходном вопросе это требование не указывалось. 20.02.2015
  • извините, отредактировал мой вопрос и добавил это требование в 20.02.2015
  • Для нового вопроса я рекомендую использовать BFS вместо DFS. Это дает вам кратчайший путь, гарантирующий отсутствие напрасной прогулки. 20.02.2015
  • Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

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

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..