WedX - журнал о программировании и компьютерных науках

Как найти самый короткий путь в таблице маршрутов полетов

У меня проблемы с получением справки о всех пересадках рейса.

У меня есть таблица с маршрутами полета, как показано ниже, в которой есть аэропорт-источник и аэропорт назначения. Теперь я хочу получить кратчайшие рейсы (наименьшее количество остановок в пути) из аэропорта A в аэропорт B, прямого маршрута из A в B нет, поэтому мне нужно соединить несколько маршрутов вместе.

Так, например, если я хочу перейти с 18 на 1403, я хочу получить маршруты

(18 > 24 | 24 > 87 | 87 > 1403) 

и не

(18 > 24 | 24 > 87 | 87 > 99| 99 > 1403)

Вот некоторые тестовые данные

src_apid | dst_apid
---------+----------
18       | 24
24       | 87
87       | 99
87       | 1403
99       | 18
99       | 1403

Моя попытка выглядит так:

WITH rejkabrest (
src_apid,
dst_apid
) AS (
SELECT
    src_apid,
    dst_apid
FROM
    routes
WHERE
    src_apid = 18  
UNION ALL 
SELECT
    a.src_apid,
    a.dst_apid
FROM
    routes a
    INNER JOIN rejkabrest b ON a.src_apid = b.dst_apid
    WHERE b.dst_apid = 1403
) SELECT
src_apid, dst_apid
FROM
rejkabrest;

Но таким образом я получаю только все маршруты, которые начинаются в аэропорту-источнике 18. И если я попробую другой способ, у меня возникнет проблема с петлей.

Надеюсь, вы, ребята, можете мне помочь. Спасибо заранее!

19.04.2017

  • docs.oracle.com/cd/B19306_01/server.102/ b14200 / query003.htm 19.04.2017
  • Этот ответ представляет собой синтаксис SQL-Server, но может указать вам путь. Основная хитрость состоит в том, чтобы нести растущую строку со всеми посещенными станциями и останавливать рекурсию, если одно место повторно посещается. 19.04.2017

Ответы:


1

Используйте connect by nocycle и функцию rank():

select path
  from (
    select r.*, rank() over (order by lvl) rnk
      from (select routes.*, level lvl, 
                   sys_connect_by_path(src_apid, '->')||'->'||dst_apid path
              from routes 
              connect by nocycle src_apid = prior dst_apid and src_apid <> 1403
              start with src_apid = 18) r
      where dst_apid = 1403 )
  where rnk = 1

Демо:

with routes (src_apid, dst_apid ) as (
    select 18,   24 from dual union all
    select 24,   87 from dual union all
    select 87,   99 from dual union all
    select 87, 1403 from dual union all
    select 99,   18 from dual union all
    select 99, 1403 from dual )
select path
  from (
    select r.*, rank() over (order by lvl) rnk
      from (select routes.*, level lvl, 
                   sys_connect_by_path(src_apid, '->')||'->'||dst_apid path
              from routes 
              connect by nocycle src_apid = prior dst_apid and src_apid <> 1403
              start with src_apid = 18) r
      where dst_apid = 1403 )
  where rnk = 1

PATH
--------------------
->18->24->87->1403
19.04.2017

2

Вот один из способов рекурсивного построения пути. Используйте предложение CYCLE, чтобы избежать исключений цикла. Вы получите кратчайший путь из найденных путей с Oracle KEEP FIRST.

with cte (dst_apid, path, stops) as
(
  select dst_apid, src_apid || ' > ' || dst_apid as path, 0 as stops
  from routes
  where src_apid = 18  
  union all 
  select r.dst_apid, cte.path || ' > ' || r.dst_apid, cte.stops + 1
  from cte 
  join routes r on r.src_apid = cte.dst_apid
  where cte.dst_apid <> 1403
)
cycle dst_apid set cycle to 1 default 0
select max(path) keep (dense_rank first order by stops)
from cte
where cte.dst_apid = 1403;

Помимо KEEP FIRST это стандартный SQL. Вместо этого вы можете использовать SELECT path FROM cte WHERE dst_apid = 1403 FETCH FIRST 1 ROW ONLY, чтобы сделать этот стандарт совместимым. Oracle поддерживает этот синтаксис с 12c.

19.04.2017
  • Привет, спасибо за ответ. Возможно, вы захотите изменить остановки на cte.stops в строке 7 для будущих читателей (иначе это не сработает, по крайней мере, для меня). Мне не удалось выполнить запрос, так как в моей таблице тысячи строк. Можно ли добавить максимальное количество остановок между рейсами для повышения производительности? Подождав 30 минут, я все еще не получил никакого результата: - / 20.04.2017
  • Что ж, у меня это работает без квалификатора, но я добавлю его, поскольку это, очевидно, зависит от версии Oracle (или у вас есть столбец с именем stop в таблице маршрутов). Конечно, вы можете ограничить количество остановок, например: где cte.dst_apid <> 1403 and cte.stops < 4 в cte. (cte.stops < 4 ограничивается 4 остановками; используйте любое число, которое хотите.) 20.04.2017

  • 3

    Если вам нужна строка на рейс, единственное решение, которое приходит на ум, - это два рекурсивных запроса. Первый строит маршруты полетов с номерами 1, 1.1, 1.2, 1.1.1 и т.д .; Секунды собирают рейсы, принадлежащие одному маршруту. Довольно сложно:

    with cte1 (routepart, pos, src_apid, dst_apid) as
    (
        select to_char(rownum) as routepart, 1 as pos, src_apid, dst_apid
        from routes
        where src_apid = 18  
      union all 
      select cte1.routepart || '-' || rownum, pos + 1, r.src_apid, r.dst_apid
      from cte1 
      join routes r on r.src_apid = cte1.dst_apid
      where cte1.dst_apid <> 1403
    )
    cycle src_apid set cycle to 1 default 0
    , cte2 (route, routepart, pos, src_apid, dst_apid) as
    (
      select routepart as route, routepart, pos, src_apid, dst_apid
      from cte1
      where dst_apid = 1403
      union all
      select cte2.route, cte1.routepart, cte1.pos, cte1.src_apid, cte1.dst_apid
      from cte1
      join cte2 on cte2.routepart like cte1.routepart || '%'
                and nvl(length(regexp_replace(cte2.routepart, '[[:digit:]]', '')), 0) =
                    nvl(length(regexp_replace(cte1.routepart, '[[:digit:]]', '')), 0) + 1
    )
    cycle src_apid set cycle to 1 default 0
    select pos, src_apid, dst_apid
    from
    (
      select
        cte2.*, 
        rank() over (order by length(regexp_replace(route, '[[:digit:]]', ''))) as rn
      from cte2
    )
    where rn = 1
    order by route, pos;
    

    Используйте ROW_NUMBER вместо RANK, если вам не нужны связи.

    19.04.2017
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]