如何在GTFS网络上优化我的寻路算法

时间:2014-12-09 13:33:31

标签: mysql database performance algorithm path-finding

简介

我使用了稍加修改的GTFS数据库。

我有一个给出两个地理位置的第一步算法:

  • 出发和到达周围的停靠点列表
  • 连接这些停靠点列表的路线列表

第二步算法找到与这些停靠点和路线匹配的最佳旅程 这对于直接旅行以及使用一个连接的旅程非常有效。

当我尝试使用2个连接寻找最佳旅程时,我的问题就出现了(因此有3个旅行需要搜索)。

数据库

GTFS格式具有以下表格(每个表格都有一个指向此列表中上一个/下一个表格的外键):

  • stops:停止信息(地理位置,名称等)
  • stop_times:时间表
  • trips:车辆(公共汽车,地铁等)的行程
  • routes:大致采用相同路径的旅程系列(例如同一路线上的标准和快速旅行,但采取了不同的停靠点)

我添加了以下表格

  • stop_connectionsstopstop个关联(约1至20个)
  • stops_routes:列出每routes
  • 的可用stop

这是我在一个城市的表行数,我得到的结果很慢(巴黎,法国):

  • stops:28k
  • stop_times:12M
  • trips:513k
  • routes:1k
  • stop_connections:365k
  • stops_routes:227k

算法

算法的第一步是将两个纬度/经度点作为输入,并提供:

  • 每个地点的停靠点列表
  • 可用于连接这些站点的路由(最多两个连接)

第二步采用每个开始停止,并分析仅使用第一步选择的路线的可用旅程。

这是我尝试优化的部分。以下是我查询数据库的方式:

Query schema written on paper

我的搜索字词(图中绿色):

  • 一次出发停止
  • 几个到达站点(1到20)
  • 出发时,首次连接和最后一次旅行时允许的路线
  • 服务ID(此处不相关,可以忽略)

以下是我现在所做的事情:

  1. 从停止开始=>获取时间表=>获得旅行=>得到路线;过滤允许的路线。
  2. 使用stop_connections
  3. 将第一次行程的到达站点连接到可能的站点列表
  4. 从步骤1重复两次,以便我有3次旅行/ 2次连接
  5. 问题

    这在某些情况下运行良好,但在其他情况下可能会非常慢。通常,只要我加入时间表或停止连接,返回的行就会增加10倍。由于我将这些表连接8次,因此引擎可能会搜索10 ^ 8行。

    现在我确信我可以让它更有效率。

    我的问题是每次加入时行数增加,到达终止时选择到达 我的意思是我在给定的出发时间(可能有数百万种组合)从给定的停靠点获得所有可能的旅程,并且当我的搜索到达最后一次旅行时,我可以过滤~20允许到达站点。
    如果我能以某种方式知道'它可能会快得多。很快,一条路线不值得搜索。

    优化

    这是我尝试/想到的:

    1。加入stops_routes

    时的内部联接stop_connections
      

    仅选择在下次旅行时通往允许路线的连接处的停靠点。

    当有很多连接并且并非所有连接的停靠点都很有趣时(有些连接的停靠点可能只用于我们不想要的路线),这有时会很有效。
    但是,如果没有多少连接的停靠点和许多允许的路由,则此内连接可以增加行数。

    2。对stop_times

    进行分区
      

    创建stop_times的较小副本,其中仅包含接下来两个小时左右的时间表。实际上,当我的旅程从早上8点开始时,让数据库引擎搜索时间表(例如,直到晚上10点)是没用的。只保持上午8点到上午10点就够了,而且要快得多。

    这非常有效,因为它大大减少了要搜索的行数。

    我已成功实施此功能,它将搜索时间缩短了大约10倍甚至100倍。

    3。识别出好的'并且'坏'路由

      

    在大都市区,通常有很长的路线,在长距离旅行时非常有用。但是这些路线在小距离旅行时并不是最佳选择。一个了解自己城市公共交通系统的人会很快告诉他们从这个社区到另一个社区,最好的选择是采取特定的路线。

    然而,这很难做到,并且需要在每个城市进行自定义

    我打算让这个算法完全独立于城市,所以我不太愿意走这条路

    4。使用众包来识别运行良好的路径

      

    第一次搜索速度很慢,但从中获取的信息可用于快速将结果提供给具有类似旅程的下一个人。

    然而,出发和到达站点的组合太多,从一个查询中获取的信息可能不是很有用。

    我不知道这是不是一个好主意。我还没有实现它。

    下一步

    我的想法已经不多了。我知道这不是编程问题,而是对算法的想法请求。我希望这属于SO范围。

3 个答案:

答案 0 :(得分:2)

将它放在网络上会让事情变得有趣,但从根本上说,你正在进行寻路,这是一个缓慢的过程。您正在遇到问题的指数性质,并且仅使用3个连接即可。

我有一些建议,你可以在使用mysql时使用这些建议,以及一些可能无法在其中实现的建议。

  1. 不是对时间表进行分区,而是仅针对任何给定路线进行下一次。如果您在上午8点离开,那么您是正确的,只看8-10的路线比看全部路线要好。然而,如果AB的路线在8:20,8:40,9:9,9:15,9:25,9:45离开......没有理由把它们全部拿走:只是考虑到任何一条路线的首次到达时间,因为它比其他路线严格要好。

  2. 我认为你正在修剪任何返回已经访问过的位置的路线?如果没有,你或许应该:他们对你没用。这在SQL框架中可能有些困难。

  3. 根据其覆盖范围,您可以使用(小得多)routes表找到路径,然后从trip表中找到最佳工作路径的最佳实现。

  4. 在SQL的框架内这可能是不可能的,但是使得最有效的寻路算法快速的是它们使用启发式搜索。您的搜索沿着每条可能的路线向下移动 - 首先俯视朝向正确方向的路线会快得多。如果它没有平移,则选择的方向不太可能。关键在于,只要您有结果,就可以退货 - 在您返回答案时,您有效地修剪了您尚未 搜索的每条路线。

  5. 预先计算的首选路线:您建议这需要人工干预,但我反驳说您可以通过计算方式进行。花时间正确搜索从各个点到各个其他点的路线,并检查路线工作方式的统计数据。我希望你能找到一些东西,让你在这里的任何地方都可以使用这个中间路径" table - 你的问题从"找到从A到B的路径#34;到"找到从A到C的路径,然后是从D到B"的路径。这样做有可能导致您找到次优路线(因为您从预先计算的统计数据中做出假设),但它可以让您更快地找到次优路线。在网格布局上,它根本不起作用;在集线器布局上它可以很好地工作。

答案 1 :(得分:2)

感谢zebediah49,我实现了以下算法:

0。查找表

首先,我在trip表上创建了一个唯一标识它的ID。它基于按顺序停止的列表。因此,此ID可确保具有相同ID的两次行程将采用完全相同的路线 我将此ID称为trip_type

我已经改进了我的stop_connections表格,因此它包含了费用。这用于选择两个来自'的最佳连接。停靠点连接到相同的'到'停止。

1。从出发站开始旅行

将这些旅行限制为每次旅行类型只有1次(group by trip_type

2。从这些旅行中获取抵达

如果有两次行程到达同一站点,则仅选择最佳行程

3。从这些到达站点获取连接站点

如果有> 1个停靠点连接到同一站点

,则仅选择最佳连接

4。从步骤1开始重复

我已将其拆分为多个子查询和临时表,因为我可以轻松地对每个步骤中的最佳停靠/行程进行分组和过滤。这可确保将最小搜索量发送到SQL服务器。

我已将此算法存储到SQL过程中,该过程将在单个SQL语句中执行此操作:

call Get2CJourneys(dt, sd, sa, r1, r2, r3)

其中:

  • dt:出发时间
  • sd:在出发点停止
  • sa:在抵达点停留
  • r1r2r3:允许第1,第2和第3次旅行的路线

过程调用在< 600ms中返回有趣的结果,我的上一个算法在几分钟内返回相同的结果。

答案 2 :(得分:-1)

扩展@ zebedia49的第四点,您可以预先计算路线行进的矢量,例如:正北方向的路线的矢量为0,正西= 90,正南= 180,正东= 270.仅返回路线,其矢量在距离乌鸦的360度模数范围内+/- 15 - 飞行路线(如果+/- 15查询没有返回任何命中,则为+/- 30)。