原题地址 。
— 第9天:编码错误 —
当你的邻座正开心地玩着电子游戏时,你把注意力转向了面前座位小屏幕上一个开放的数据端口。
尽管这个端口是非标准的,但你巧妙地用几个回形针成功地将它连接到了你的电脑上。连接后,端口输出了一系列数字(你的谜题输入)。
这些数据似乎是用XMAS(交换掩码加法系统)加密的,对你来说幸运的是,这是一种存在重要弱点的旧密码。
XMAS 首先传输一个包含25个数字的前导码。之后,你接收到的每个数字都应该是紧邻的前25个数字中任意两个数字的和。这两个数字的值必须不同,并且可能不止存在一对这样的数字。
例如,假设你的前导码由1到25的数字随机排列组成。那么,为了有效,下一个数字必须是这些数字中某两个数字的和:
26将是一个有效的下一个数字,因为它可以是1加25(或者许多其他组合,如2和24)。49将是一个有效的下一个数字,因为它是24和25的和。100将是无效的;前25个数字中没有两个数字的和是100。50也是无效的;尽管25出现在前25个数字中,但这对数字中的两个数必须是不同的。
假设第26个数字是 45,并且第一个数字(由于已超过25个数字之前的范围,不再是可选项)是 20。那么,为了使下一个数字有效,需要在 1-19、21-25 或 45 这些可用的数字中,存在一对数字的和等于它:
26仍然是一个有效的下一个数字,因为1和25仍然在前25个数字的范围内。65将是无效的,因为没有两个可用数字的和等于它。64和66都是有效的,因为它们分别是19+45和21+45的结果。
下面是一个更大的例子,它只考虑前5个数字(并且前导码长度为5):
35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576
在这个例子中,在5个数字的前导码之后,几乎每个数字都是前5个数字中某两个数字的和;唯一不遵循此规则的数字是 127。
攻击XMAS数据弱点的第一步是找出列表中(前导码之后)第一个不是其前25个数字中某两个数字之和的数字。第一个不具备此属性的数字是什么?
— 第二部分 —
破解XMAS加密的最后一步依赖于你刚刚找到的无效数字:你必须在列表中找到一个连续的、至少包含两个数字的集合,这些数字的和等于第一步中找到的无效数字。
再次考虑上面的例子:
35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576
在这个列表中,将从 15 到 40 的所有数字相加,就得到了第一步中的无效数字 127。(当然,你实际列表中的连续数字集合可能更长。)
为了找到加密弱点,将这个连续范围内最小和最大的数字相加;在这个例子中,它们是 15 和 47,得到 62。
在你的XMAS加密数字列表中,加密弱点是什么?
解题思路:
第一部分就是穷举每个数字之前所有相邻的五个数字两两相加之和,用标量子查询标记出不在前面五个数字之和之中的那些。 注意必须包含select 1 where exists,才能保证返回一行。
第二部分要求算出每个数字之前所有相邻的n个数字相加之和, 这可以用带范围的分析函数完成,从current row 开始,用following控制数字的个数,然后找所有n个数字和中能和刚才第一部分找到的数相等的组合,用开始位置和长度确定范围,再找出其中的最大和最小值加起来。
两部分的代码写在了一起,第一部分的结果保存在 fd子查询中。
with recursive t as(select '35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576' t),
b as(select row_number()over()rn,b::bigint b from(select unnest(string_split(t,chr(10)))b from t))
, a as(select bm.rn , bm.b,
(select 1 where exists
(select 1 from (select b2.b from b b2 where b2.rn between bm.rn-5 and bm.rn-1) ta,
(select b2.b from b b2 where b2.rn between bm.rn-5 and bm.rn-1) tb where bm.b=ta.b+tb.b and ta.b>tb.b)
) can
from b bm
)
, fd as(select * from a where can is null and rn>5 order by rn limit 1)
, d as(select 0 lv,0 rn,0::bigint sumc
union all
select lv+1,bm.rn, sum(bm.b)over(order by bm.rn rows between current row and lv+1 following) sumc from b bm,(select * from d limit 1) where
not exists(select 1 from d where sumc=(select b from fd))
)
,fd2 as(select rn,rn+lv rn2 from d where sumc=(select b from fd))
select min(b)+max(b) from b where rn between (select rn from fd2) and (select rn2 from fd2);
以下是输出
--显示从当前位置开始连续4个数的和
memory D .read 2009.txt
┌───────┬───────┬──────────────────────────────────────────────────────────────────────────┐
│ rn │ b │ sum(bm.b) OVER (ORDER BY bm.rn ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING) │
│ int64 │ int64 │ int128 │
├───────┼───────┼──────────────────────────────────────────────────────────────────────────┤
│ 1 │ 35 │ 95 │
│ 2 │ 20 │ 107 │
│ 3 │ 15 │ 127 │<----
│ 4 │ 25 │ 174 │
│ 5 │ 47 │ 204 │
│ 6 │ 40 │ 222 │
│ 7 │ 62 │ 277 │
│ 8 │ 55 │ 317 │
│ 9 │ 65 │ 379 │
│ 10 │ 95 │ 464 │
│ 11 │ 102 │ 551 │
│ 12 │ 117 │ 576 │
│ 13 │ 150 │ 678 │
│ 14 │ 182 │ 827 │
│ 15 │ 127 │ 922 │
│ 16 │ 219 │ 1104 │
│ 17 │ 299 │ 1461 │
│ 18 │ 277 │ 1162 │
│ 19 │ 309 │ 885 │
│ 20 │ 576 │ 576 │
├───────┴───────┴──────────────────────────────────────────────────────────────────────────┤
│ 20 rows 3 columns │
└──────────────────────────────────────────────────────────────────────────────────────────┘
memory D .read 2009.txt
┌───────┬───────┬───────┐
│ lv │ rn │ sumc │
│ int32 │ int32 │ int32 │
├───────┼───────┼───────┤
│ 3 │ 3 │ 127 │
└───────┴───────┴───────┘
memory D .read 2009.txt
┌───────────────────┐
│ (min(b) + max(b)) │
│ int64 │
├───────────────────┤
│ 62 │
└───────────────────┘
memory D .read 2009.txt
┌────────┬────────┐
│ min(b) │ max(b) │
│ int64 │ int64 │
├────────┼────────┤
│ 15 │ 47 │
└────────┴────────┘

738

被折叠的 条评论
为什么被折叠?



