Part One
根据第一行的方向,从AAA路由到ZZZ
RL
AAA = (BBB, CCC)
BBB = (DDD, EEE)
第一部分比较简单,简单的从AAA持续路由下去,直到ZZZ终止。
WITH recursive rule AS (
SELECT line
FROM lance_input
LIMIT 1
),
origin AS (
SELECT key, substring(lr, 2, 3) as l, substring(lr, 7, 3) as r
FROM (
SELECT line, trim(split_part(line, ' = ', 1)) as key, trim(split_part(line, ' = ', 2)) as lr
FROM lance_input
OFFSET 2
) t
),
path AS (
SELECT key, case when direction = 'L' then l when direction = 'R' then r end as next_key, 1 as step, direction
FROM (
SELECT key, l, r, substring(line, 1, 1) as direction
FROM origin, rule
WHERE key = 'AAA'
LIMIT 1
) t
UNION ALL
SELECT key, case when substring(line, idx, 1) = 'L' then l when substring(line, idx, 1) = 'R' then r end as next_key, step + 1 as step, substring(line, idx, 1)
FROM (
SELECT x.key, x.l, x.r, y.step % length(line) + 1 as idx, line, step
FROM origin x
JOIN path y
ON x.key = y.next_key
AND y.next_key != 'ZZZ'
JOIN rule
ON 1 = 1
) t
)
SELECT * FROM PATH;
Part Two
从多个A结尾的起点,路由到每个终点都是Z结尾
11A = (11B, XXX)
11B = (XXX, 11Z)
11Z = (11B, XXX)
22A = (22B, XXX)
Multiple Origins
起点是多条记录,迭代的终止条件,就是所有的key都是Z结尾。这里需要使用到sum over的开窗函数
WITH p AS (
SELECT *, sum(1) over () as total,
sum(CASE WHEN next_key like '%Z' THEN 1 ELSE 0 END) over () as zcnt
FROM PATH
)
完整SQL如下所示:
WITH recursive rule AS (
SELECT line
FROM lance_input
LIMIT 1
),
origin AS (
SELECT key, substring(lr, 2, 3) as l, substring(lr, 7, 3) as r
FROM (
SELECT line, trim(split_part(line, ' = ', 1)) as key, trim(split_part(line, ' = ', 2)) as lr
FROM lance_input
OFFSET 2
) t
),
path AS (
SELECT key, case when direction = 'L' then l when direction = 'R' then r end as next_key, 1 as step, direction
FROM (
SELECT key, l, r, substring(line, 1, 1) as direction
FROM origin, rule
WHERE key like '%A'
) t
UNION ALL
SELECT key, case when substring(line, idx, 1) = 'L' then l when substring(line, idx, 1) = 'R' then r end as next_key, step + 1 as step, substring(line, idx, 1)
FROM (
WITH p AS (
SELECT *, sum(1) over () as total, sum(CASE WHEN next_key like '%Z' THEN 1 ELSE 0 END) over () as zcnt
FROM PATH
)
SELECT x.key, x.l, x.r, y.step % length(line) + 1 as idx, line, step
FROM origin x
JOIN p y
ON x.key = y.next_key
AND y.total != y.zcnt
JOIN rule
ON 1 = 1
) t
)
SELECT * FROM PATH;
key | next_key | step | direction
-----+----------+------+-----------
11A | 11B | 1 | L
22A | 22B | 1 | L
11B | 11Z | 2 | R
22B | 22C | 2 | R
11Z | 11B | 3 | L
22C | 22Z | 3 | L
11B | 11Z | 4 | R
22Z | 22B | 4 | R
11Z | 11B | 5 | L
22B | 22C | 5 | L
11B | 11Z | 6 | R
22C | 22Z | 6 | R
(12 rows)
Endless Iterations
但是在真正运行的时候,运行了很长时间都没有结果。运行了千万都还没有结束。
postgres-# SELECT * FROM PATH limit 1 offset 10000000;
key | next_key | step | direction
-----+----------+---------+-----------
GLV | MDV | 1666667 | L
(1 row)
那就以其中一个A为起点,看看中途到底是否经历过Z
AAA起点的记录
key | next_key | step | direction
-----+----------+-------+-----------
PLJ | ZZZ | 20221 | R
PLJ | ZZZ | 40442 | R
PLJ | ZZZ | 60663 | R
PLJ | ZZZ | 80884 | R
(4 rows)
HCA起点的记录
key | next_key | step | direction
-----+----------+-------+-----------
MJM | MTZ | 18559 | R
MJM | MTZ | 37118 | R
MJM | MTZ | 55677 | R
MJM | MTZ | 74236 | R
MJM | MTZ | 92795 | R
(5 rows)
明显看出,A开头的Z结尾,形成了一个循环,看来输入是故意这么设计的。Steps以L开始,R结尾。第一步A的mapping和最后Z的mapping恰好相反,从而在下一个循环中保证后续Steps是相同的。
Steps的总长度是277,而20221、18559这些恰好是277的倍数。
AAA = (QXT, CDL)
ZZZ = (CDL, QXT)
HCA = (LJP, GVX)
MTZ = (GVX, LJP)
LCM(Least Common Multiple)
那只要求出每个A的循环Steps长度的最小公倍数,在那一步的时候,所有的下一步都是Z结尾。
postgres-# SELECT next_key,step, step / 277 from (SELECT * FROM PATH limit 1000000) t where next_key like '%Z' order by 2;
next_key | step | ?column?
----------+--------+----------
FPZ | 13019 | 47
MLZ | 14681 | 53
DPZ | 16897 | 61
MTZ | 18559 | 67
CPZ | 19667 | 71
ZZZ | 20221 | 73
47 * 53 * 61 * 67 * 71 * 73 * 277