楼主: 〇〇

一个求最短总路径的问题

[复制链接]
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
11#
发表于 2021-8-6 21:36 | 只看该作者
既然是经典问题,就应该有前人研究出来的算法,比如:
https://blog.forec.cn/2015/09/23/Graph-Algorithms4/

如果用SQL那只能暴力求出所有路径的组合然后去重复并计算总成本。

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
12#
发表于 2021-8-7 02:33 | 只看该作者
用SQL暴力算了一小时:

create table p2 as
with d as (
select 1 id,1 d from dual union all
select 2,23 from dual union all
select 3,34 from dual union all
select 4,125 from dual union all
select 5,145 from dual )
,p as (
select cast(sys_connect_by_path(from_id||':'||to_id||':'||len,',') as varchar2(1000)) as path,to_id
  from trip
where to_id in (select d from d)
start with from_id=12345
connect by from_id=prior to_id
)
,p2 (path,to_id,cnt) as (
select cast(path as varchar2(2000)),to_id,d.id from p,d where p.to_id=d.d and d.id=1
union all
select cast(p2.path||p.path as varchar2(2000)),p.to_id,p2.cnt+1
from p2,p,d
where p2.cnt+1=d.id and p.to_id=d.d
)
select * from p2 where cnt=5;


select * from (
select *
from p2
     ,lateral(
       select sum(distance) as distance
         from ( select distinct from_id,to_id,distance
                  from (select regexp_substr(node,'[^:]+',1,1) from_id,regexp_substr(node,'[^:]+',1,2) to_id,regexp_substr(node,'[^:]+',1,3) distance
                          from ( select regexp_substr(p2.path,'[^,]+',1,level) as node
                                  from dual
                                  connect by level<=regexp_count(p2.path,',')
                               )
                       )
              )
      )
order by distance
)
where rownum=1;

PATH
-----------------------------------------------------------------------------------------------------------------------------
     TO_ID        CNT   DISTANCE
---------- ---------- ----------
,12345:125:30,125:1:0,12345:1234:49,1234:234:0,234:23:1,12345:1234:49,1234:134:0,134:34:0,12345:125:30,12345:145:50
       145          5        130


Elapsed: 00:00:54.68

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
13#
 楼主| 发表于 2021-8-7 06:06 来自手机 | 只看该作者
谢谢帮助

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
14#
 楼主| 发表于 2021-8-7 19:42 | 只看该作者
distinct很巧妙,但是笛卡尔积,有没有办法中间剪枝

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
15#
 楼主| 发表于 2021-8-7 19:44 | 只看该作者
另外,看时间是54秒,怎么是1小时?
我的p2改用cte很快
create table p2 as
with d as (
select 1 id,1 d union all
select 2,23 union all
select 3,34 union all
select 4,125 union all
select 5,145 ),
p(path,to_id) as
(select from_id||':'||to_id||':'||len ,to_id from trip where from_id=12345
union all
select p.path||','||trip.from_id||':'||trip.to_id||':'||trip.len,trip.to_id from trip,p
where trip.from_id=p.to_id)
select * from p where to_id in (select d from d);

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
16#
发表于 2021-8-7 21:54 来自手机 | 只看该作者
我看错了,应该是一分钟。我弄完了就没管它,一小时后才想起去看结果。最初写法是一整个SQL,但是12c会崩溃,所以先建了临时表。我的算法是事后求组合,所以是没法剪枝的。在生成路径的过程中可以剪枝但用SQL没法做。先用Dijkstra 算法求出每个点到每个目的地的代价,在路径分叉处穷尽所有人分组的组合取最小。分叉后的成本计算用递归函数。

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
17#
 楼主| 发表于 2021-8-8 08:11 | 只看该作者
从123456开始的6级数据

insert2.txt

33.84 KB, 下载次数: 2

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
18#
 楼主| 发表于 2021-8-8 18:14 | 只看该作者
把newkid的改写成postgres
create table p as
with RECURSIVE d as (
select 1 id,1 d union all
select 2,23 union all
select 3,34 union all
select 4,125 union all
select 5,145 ),
p(path,to_id) as
(select from_id||':'||to_id||':'||len ,to_id from trip where from_id=12345
union all
select p.path||','||trip.from_id||':'||trip.to_id||':'||trip.len,trip.to_id from trip,p
where trip.from_id=p.to_id)
select * from p where to_id in (select d from d);

SELECT 107
时间:23.861 ms

create table p2 as
with RECURSIVE d as (
select 1 id,1 d union all
select 2,23 union all
select 3,34 union all
select 4,125 union all
select 5,145 ),
p2 (path,to_id,cnt) as (
select cast(path as varchar(2000)),to_id,d.id from p,d where p.to_id=d.d and d.id=1
union all
select cast(p2.path||','||p.path as varchar(2000)),p.to_id,p2.cnt+1
from p2,p,d
where p2.cnt+1=d.id and p.to_id=d.d
)
select * from p2 where cnt=5;

SELECT 114075
Time: 2359.945 ms (00:02.360)

with d as
(select p2.*,regexp_split_to_table(path,',') node from p2)
,d2 as
(select d.*,regexp_split_to_array(node,':') arr from d)
select * from
(
select path,sum(dis) distance
from(select distinct path, arr[1] from_id,arr[2] to_id,arr[3]::int dis from d2)y
group by path)x
order by distance
limit 1;
                                                              path                                                              | distance
--------------------------------------------------------------------------------------------------------------------------------+----------
12345:1234:49,1234:123:0,123:1:0,12345:1234:49,1234:234:0,234:23:1,12345:1234:49,1234:134:0,134:34:0,12345:125:30,12345:145:50 |   130
(1 行记录)


Time: 73903.664 ms (01:13.904)

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
19#
 楼主| 发表于 2021-8-8 20:09 | 只看该作者
把distinct操作提前到node,快了很多
with d as
(select p2.*,unnest(array(select distinct unnest(regexp_split_to_array(path,',')))) node from p2)
,d2 as
(select d.*,regexp_split_to_array(node,':') arr from d)
select * from
(
select path,sum(dis) distance
from(select path, arr[1] from_id,arr[2] to_id,arr[3]::int dis from d2)y
group by path)x
order by distance
limit 1;
Time: 15025.549 ms (00:15.026)

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
20#
 楼主| 发表于 2021-8-8 20:34 | 只看该作者
6级,p2创建失败
create table p as
with RECURSIVE d as (
select 1 id,1 d union all
select 2,23 union all
select 3,34 union all
select 4,125 union all
select 5,145 ),
p(path,to_id) as
(select from_id||':'||to_id||':'||len ,to_id from trip where from_id=123456
union all
select p.path||','||trip.from_id||':'||trip.to_id||':'||trip.len,trip.to_id from trip,p
where trip.from_id=p.to_id)
select * from p where to_id in (select d from d);

SELECT 717
时间:34.603 ms

create table p2 as
with RECURSIVE d as (
select 1 id,1 d union all
select 2,23 union all
select 3,34 union all
select 4,125 union all
select 5,145 ),
p2 (path,to_id,cnt) as (
select cast(path as varchar(2000)),to_id,d.id from p,d where p.to_id=d.d and d.id=1
union all
select cast(p2.path||','||p.path as varchar(2000)),p.to_id,p2.cnt+1
from p2,p,d
where p2.cnt+1=d.id and p.to_id=d.d
)
select * from p2 where cnt=5;
ERROR:  could not write to file "base/pgsql_tmp/pgsql_tmp8000.55": No space left on device
Time: 36069.743 ms (00:36.070)

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表