利用Duckdb求解Advent of Code 2021第3题 二进制诊断 第2部分

DuckDB递归CTE解AoC二进制筛选

第二部分每次筛选的条件都和前一次相关,递归CTE在这里最合适,一开始我把条件s >=c-s写成了s>=c//2, 貌似等价,其实不然,当c为奇数,除法截断取整有误差,比如s=6,c=13,s >=c-s不成立,而s>=c//2成立,导致错误,结果用了很多时间调试,这警示我们要严格按需求编写。

with recursive t as(select '00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010' t),
a as(select unnest(string_split(t,chr(10)))a from t),
b as(select 1 lv,a,(select case when s >=c-s then 1 else 0 end from( select sum(substr(a,1,1)::int)s,count(*)c from a))b from a
union all
select lv+1,a, (select case when s >=c-s then 1 else 0 end from( select sum(substr(a,lv+1-case when lv=5 then 1 else 0 end,1)::int)s,count(*)c from b where substr(a,lv,1)=b)) from b where lv<5 and substr(a,lv,1)=b),

bb as(select * from b where lv=(select max(lv)from b)and substr(a,lv,1)=b), 

b2 as(select 1 lv,a,(select case when s <c-s then 1 else 0 end from( select lv,sum(substr(a,1,1)::int)s,count(*)c from a))b from a
union all
select lv+1,a, (select case when s <c-s then 1 else 0 end from( select sum(substr(a,lv+1-case when lv=5 then 1 else 0 end,1)::int)s,count(*)c from b2 where substr(a,lv,1)=b)) from b2 where lv<5 and substr(a,lv,1)=b), 

bb2 as(select * from b2 where lv=(select max(lv)from b2)and (substr(a,lv,1)=b or 1=(select count(*) from b2 where lv=(select max(lv)from b2)) ))

select bb.a, bb2.a, bb.a::BITSTRING::int * bb2.a::BITSTRING::int from bb, bb2
;

总体思路是用一个b列保存当前层次对应的子字符串的出现最多次、最少次的值,首次就用全表统计,然后在迭代中用上次的结果过滤,同时生成下次要用的值。最后一步筛选无法在递归中完成,单独用子查询bb、bb2得到。
上述代码适用于示例的五位数,对于真实的输入,把代码中的5替换成12即可。
DuckDB允许在递归CTE中使用子查询、聚合函数等,大大方便了实现。
以下是递归查询产生的b表和bb表的结果,可以看出逐步筛选的过程。

┌───────┬─────────┬───────┐
│  lv   │    a    │   b   │
│ int32 │ varchar │ int32 │
├───────┼─────────┼───────┤
│     1 │ 00100   │     1 │
│     1 │ 11110   │     1 │
│     1 │ 10110   │     1 │
│     1 │ 10111   │     1 │
│     1 │ 10101   │     1 │
│     1 │ 01111   │     1 │
│     1 │ 00111   │     1 │
│     1 │ 11100   │     1 │
│     1 │ 10000   │     1 │
│     1 │ 11001   │     1 │
│     1 │ 00010   │     1 │
│     1 │ 01010   │     1 │
│     2 │ 11110   │     0 │
│     2 │ 10110   │     0 │
│     2 │ 10111   │     0 │
│     2 │ 10101   │     0 │
│     2 │ 11100   │     0 │
│     2 │ 10000   │     0 │
│     2 │ 11001   │     0 │
│     3 │ 10110   │     1 │
│     3 │ 10111   │     1 │
│     3 │ 10101   │     1 │
│     3 │ 10000   │     1 │
│     4 │ 10110   │     1 │
│     4 │ 10111   │     1 │
│     4 │ 10101   │     1 │
│     5 │ 10110   │     1 │
│     5 │ 10111   │     1 │
├───────┴─────────┴───────┤
│ 28 rows       3 columns │
└─────────────────────────┘
┌───────┬─────────┬───────┐
│  lv   │    a    │   b   │
│ int32 │ varchar │ int32 │
├───────┼─────────┼───────┤
│   5   │ 10111   │   1   │
└───────┴─────────┴───────┘
┌───────┬─────────┬───────┐
│  lv   │    a    │   b   │
│ int32 │ varchar │ int32 │
├───────┼─────────┼───────┤
│     1 │ 00100   │     0 │
│     1 │ 11110   │     0 │
│     1 │ 10110   │     0 │
│     1 │ 10111   │     0 │
│     1 │ 10101   │     0 │
│     1 │ 01111   │     0 │
│     1 │ 00111   │     0 │
│     1 │ 11100   │     0 │
│     1 │ 10000   │     0 │
│     1 │ 11001   │     0 │
│     1 │ 00010   │     0 │
│     1 │ 01010   │     0 │
│     2 │ 00100   │     1 │
│     2 │ 01111   │     1 │
│     2 │ 00111   │     1 │
│     2 │ 00010   │     1 │
│     2 │ 01010   │     1 │
│     3 │ 01111   │     0 │
│     3 │ 01010   │     0 │
│     4 │ 01010   │     0 │
├───────┴─────────┴───────┤
│ 20 rows       3 columns │
└─────────────────────────┘
memory D .read 2103p2.txt
┌───────┬─────────┬───────┐
│  lv   │    a    │   b   │
│ int32 │ varchar │ int32 │
├───────┼─────────┼───────┤
│   4   │ 01010   │   0   │
└───────┴─────────┴───────┘

.read 2103p2t.txt
┌─────────┬─────────┬────────────────────────────────────────────────────────────────────────────┐
│    a    │    a    │ (CAST(CAST(bb.a AS BIT) AS INTEGER) * CAST(CAST(bb2.a AS BIT) AS INTEGER)) │
│ varchar │ varchar │                                   int32                                    │
├─────────┼─────────┼────────────────────────────────────────────────────────────────────────────┤
│ 10111   │ 01010   │                                    230                                     │
└─────────┴─────────┴────────────────────────────────────────────────────────────────────────────┘

我们也可以不用递归,用手工方法列出卷积的分组结果,肉眼从中找出每层出现最多或最少的值。以下示例仅列出每轮出现最多次值的筛选过程。

select substr(a,1,1)a1,substr(a,2,1)a2,substr(a,3,1)a3,substr(a,4,1)a4,substr(a,5,1)a5,count(*)c from a 
group by rollup(a1,a2,a3,a4,a5) order by a1,a2,a3,a4,a5;
┌─────────┬─────────┬─────────┬─────────┬─────────┬───────┐
│   a1    │   a2    │   a3    │   a4    │   a5    │   c   │
│ varchar │ varchar │ varchar │ varchar │ varchar │ int64 │
├─────────┼─────────┼─────────┼─────────┼─────────┼───────┤
│ 0       │ 0       │ 0       │ 1       │ 0       │     1 │
│ 0       │ 0       │ 0       │ 1       │ NULL    │     1 │
│ 0       │ 0       │ 0       │ NULL    │ NULL    │     1 │
│ 0       │ 0       │ 1       │ 0       │ 0       │     1 │
│ 0       │ 0       │ 1       │ 0       │ NULL    │     1 │
│ 0       │ 0       │ 1       │ 1       │ 1       │     1 │
│ 0       │ 0       │ 1       │ 1       │ NULL    │     1 │
│ 0       │ 0       │ 1       │ NULL    │ NULL    │     2 │
│ 0       │ 0       │ NULL    │ NULL    │ NULL    │     3 │
│ 0       │ 1       │ 0       │ 1       │ 0       │     1 │
│ 0       │ 1       │ 0       │ 1       │ NULL    │     1 │
│ 0       │ 1       │ 0       │ NULL    │ NULL    │     1 │
│ 0       │ 1       │ 1       │ 1       │ 1       │     1 │
│ 0       │ 1       │ 1       │ 1       │ NULL    │     1 │
│ 0       │ 1       │ 1       │ NULL    │ NULL    │     1 │
│ 0       │ 1       │ NULL    │ NULL    │ NULL    │     2 │
│ 0       │ NULL    │ NULL    │ NULL    │ NULL    │     5 │
│ 1       │ 0       │ 0       │ 0       │ 0       │     1 │
│ 1       │ 0       │ 0       │ 0       │ NULL    │     1 │
│ 1       │ 0       │ 0       │ NULL    │ NULL    │     1 │
│ 1       │ 0       │ 1       │ 0       │ 1       │     1 │
│ 1       │ 0       │ 1       │ 0       │ NULL    │     1 │
│ 1       │ 0       │ 1       │ 1       │ 0       │     1 │
│ 1       │ 0       │ 1       │ 1       │ 1       │     1 │<---第5步,1和0一样多,按题意选1
│ 1       │ 0       │ 1       │ 1       │ NULL    │     2 │<---第4步,1多
│ 1       │ 0       │ 1       │ NULL    │ NULL    │     3 │<---第3步,1多
│ 1       │ 0       │ NULL    │ NULL    │ NULL    │     4 │<---第2步,0多
│ 1       │ 1       │ 0       │ 0       │ 1       │     1 │
│ 1       │ 1       │ 0       │ 0       │ NULL    │     1 │
│ 1       │ 1       │ 0       │ NULL    │ NULL    │     1 │
│ 1       │ 1       │ 1       │ 0       │ 0       │     1 │
│ 1       │ 1       │ 1       │ 0       │ NULL    │     1 │
│ 1       │ 1       │ 1       │ 1       │ 0       │     1 │
│ 1       │ 1       │ 1       │ 1       │ NULL    │     1 │
│ 1       │ 1       │ 1       │ NULL    │ NULL    │     2 │
│ 1       │ 1       │ NULL    │ NULL    │ NULL    │     3 │
│ 1       │ NULL    │ NULL    │ NULL    │ NULL    │     7 │<---第1步,1多
│ NULL    │ NULL    │ NULL    │ NULL    │ NULL    │    12 │
├─────────┴─────────┴─────────┴─────────┴─────────┴───────┤
│ 38 rows                                       6 columns │
└─────────────────────────────────────────────────────────┘
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值