代码的出现2024年-24日题目《交叉电线》解答

原文地址:https://adventofcode.com/2024/day/24

第 24 天:交叉线路

你和历史学家们到达了丛林某处一片大型林地的边缘。在上次事件之后,精灵们安装了一个用于监控果实的小型设备。当历史学家们搜索林地时,其中一人问你是否可以查看一下这个监控设备;显然,它最近一直运行不正常。

该设备似乎试图通过一些布尔逻辑门来生成一个数字。每个门有两个输入和一个输出。这些门都在值为真(1)或假(0)的情况下运行。

  • AND 门:如果两个输入都为 1,则输出 1;如果任一输入为 0,则输出 0。
  • OR 门:如果一个或两个输入为 1,则输出 1;如果两个输入都为 0,则输出 0。
  • XOR 门:如果输入不同,则输出 1;如果输入相同,则输出 0。

门会等待直到收到两个输入后才产生输出;线路可以携带 0、1 或根本没有值。系统中没有循环;一旦门确定了其输出,该输出在系统重置前不会改变。每条线路最多连接一个门的输出,但可以连接到多个门的输入。

你不愿在捣鼓带电系统时冒险触电,于是记下了所有的门连接和初始线路值(你的谜题输入),以便在相对安全的环境中研究它们。例如:

x00: 1
x01: 1
x02: 1
y00: 0
y01: 1
y02: 0

x00 AND y00 -> z00
x01 XOR y01 -> z01
x02 OR y02 -> z02

由于门需要等待输入,一些线路需要以某个值开始(作为整个系统的输入)。第一部分指定了这些值。例如,x00: 1 表示名为 x00 的线路初始值为 1(就好像已经有一个门将其输出到该线路上)。

第二部分列出了所有的门及其连接的线路。例如,x00 AND y00 -> z00 描述了一个 AND 门的实例,其输入连接了线路 x00 和 y00,并将其输出写入线路 z00。

在这个例子中,模拟这些门最终导致线路 z00 上出现 0,线路 z01 上出现 0,线路 z02 上出现 1。

最终,系统试图通过组合所有以 z 开头的线路上的位来生成一个数字。z00 是最低有效位,接着是 z01,然后是 z02,依此类推。

在这个例子中,三个输出位组成了二进制数 100,等于十进制数 4。

下面是一个更大的例子:

x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
y00: 1
y01: 1
y02: 1
y03: 1
y04: 1

ntg XOR fgs -> mjb
y02 OR x01 -> tnw
kwq OR kpj -> z05
x00 OR x03 -> fst
tgd XOR rvg -> z01
vdt OR tnw -> bfw
bfw AND frj -> z10
ffh OR nrd -> bqk
y00 AND y03 -> djm
y03 OR y00 -> psh
bqk OR frj -> z08
tnw OR fst -> frj
gnj AND tgd -> z11
bfw XOR mjb -> z00
x03 OR x00 -> vdt
gnj AND wpb -> z02
x04 AND y00 -> kjc
djm OR pbm -> qhw
nrd AND vdt -> hwm
kjc AND fst -> rvg
y04 OR y02 -> fgs
y01 AND x02 -> pbm
ntg OR kjc -> kwq
psh XOR fgs -> tgd
qhw XOR tgd -> z09
pbm OR djm -> kpj
x03 XOR y03 -> ffh
x00 XOR y04 -> ntg
bfw OR bqk -> z06
nrd XOR fgs -> wpb
frj XOR qhw -> z04
bqk OR frj -> z07
y03 OR x01 -> nrd
hwm AND bqk -> z03
tgd XOR rvg -> z12
tnw OR pbm -> gnj

等待所有以 z 开头的线路上的值稳定后,该系统中各线路的值如下:

bfw: 1
bqk: 1
djm: 1
ffh: 0
fgs: 1
frj: 1
fst: 1
gnj: 1
hwm: 1
kjc: 0
kpj: 1
kwq: 0
mjb: 1
nrd: 1
ntg: 0
pbm: 1
psh: 1
qhw: 1
rvg: 0
tgd: 0
tnw: 1
vdt: 1
wpb: 0
z00: 0
z01: 0
z02: 0
z03: 1
z04: 0
z05: 1
z06: 1
z07: 1
z08: 1
z09: 1
z10: 1
z11: 0
z12: 0

将所有以 z 开头的线路的位组合起来,得到二进制数 0011111101000。将该数字转换为十进制得到 2024。

模拟这个由门和线路组成的系统。它在以 z 开头的线路上输出的十进制数是多少?

要开始,请获取你的谜题输入。

我的解答:

with recursive tb as(select substr(a, 1, 3)k, substr(a, 6, 1)::int b from read_csv('2424-input.txt',header=0)t(a) where instr(a, ':')>0)
,tl as(select a, case b when 'AND' then 1 when 'OR' then 2 when 'XOR' then 3 end o, c, e from read_csv('2424-input.txt',header=0,delim=' ')t(a,b,c,d,e))
,newtb as(select k, b from tb
	union all
select tl.e k, case o when 1 then tb.b & tb2.b  when 2 then tb.b | tb2.b  when 3 then xor(tb.b, tb2.b) end b
from newtb tb,newtb tb2,
tl where tb.k=tl.a and tb2.k=tl.c and tl.e not in (select k from newtb))	
select sum((1<<(rn-1))*b)zv from(select row_number()over(order by k)rn,b from newtb where k like 'z%');

其中各表含义如下:
tb:保存某个线路的二进制位
tl:保存计算公式
newtb:保存计算出的所有线路的二进制位,每步计算表达式左右值都已算出,具备计算条件的尚未算出的线路,
最终查询:算出的数据中以z打头的线路二进制位组成的二进制数,注意移位操作优先级低,要用括号括起再与b相乘。
对于题目当中的示例数据,如下SQL就可以求得答案,2024
但对于题目连接提供的输入数据, 上述SQL的结果为0,原因是普通的递归CTE只能引用前一步的结果,而计算表有的算式的表达式跨了多步,例如:gfj XOR gmr -> z12,而gfj和gmr不是同一步算出的,那么就无法计算。

简单的想法是在算出新数的同时,把旧数据也合并进去,可是普通CTE做不到这一点,我尝试在union all部分再次合并newtb表,结果陷入死循环。

with recursive tb as(select substr(a, 1, 3)k, substr(a, 6, 1)::int b from read_csv('2424-input.txt',header=0)t(a) where instr(a, ':')>0)
,tl as(select a, case b when 'AND' then 1 when 'OR' then 2 when 'XOR' then 3 end o, c, e from read_csv('2424-input.txt',header=0,delim=' ')t(a,b,c,d,e))
,newtb as(select k, b from tb
	union all(with tmp as(
select tl.e k, case o when 1 then tb.b & tb2.b  when 2 then tb.b | tb2.b  when 3 then xor(tb.b, tb2.b) end b
from newtb tb,newtb tb2,
tl where tb.k=tl.a and tb2.k=tl.c and tl.e not in (select k from newtb)
union all
from newtb)from tmp
))
select sum((1<<(rn-1))*b)zv from(select row_number()over(order by k)rn,b from newtb where k like 'z%');

好在DuckDB现在提供了 USING KEYrecurring.T语法,可以更新递归表中已有数据并访问全部已有数据,我把它改写成:

with recursive tb as(select substr(a, 1, 3)k, substr(a, 6, 1)::int b from read_csv('2424-input.txt',header=0)t(a) where instr(a, ':')>0)
,tl as(select a, case b when 'AND' then 1 when 'OR' then 2 when 'XOR' then 3 end o, c, e from read_csv('2424-input.txt',header=0,delim=' ')t(a,b,c,d,e))
,newtb USING KEY (k) as(select k, b from tb
	union
select tl.e k, case o when 1 then tb.b & tb2.b  when 2 then tb.b | tb2.b  when 3 then xor(tb.b, tb2.b) end b
from recurring.newtb tb,recurring.newtb tb2,
tl where tb.k=tl.a and tb2.k=tl.c and tl.e not in (select k from recurring.newtb))	
--from newtb where k like 'z%';
select sum((1<<(rn-1))*b)zv from(select row_number()over(order by k)rn,b from newtb where k like 'z%');

就算得了正确结果。注意把union all 改写成 union, 否则报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值