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

原题地址 https://adventofcode.com/2021/day/3

第 3 天:二进制诊断

潜水艇一直发出一些奇怪的嘎吱声,因此你让它生成一份诊断报告以防万一。

诊断报告(你的谜题输入)由一个二进制数字列表组成,当正确解码时,可以告诉你许多关于潜水艇状况的有用信息。首先要检查的参数是功耗。

你需要使用诊断报告中的二进制数字来生成两个新的二进制数字(称为伽马率和epsilon率)。然后,可以通过将伽马率乘以epsilon率来找到功耗。

伽马率中的每个位可以通过查找诊断报告中所有数字的对应位置中出现最多的位来确定。例如,给定以下诊断报告:

00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010

仅考虑每个数字的第一位,有五个 0 位和七个 1 位。由于出现最多的位是 1,因此伽马率的第一位是 1。

诊断报告中数字第二位出现最多的是 0,因此伽马率的第二位是 0。

第三、第四和第五位出现最多的值分别是 1、1 和 0,因此伽马率的最后三位是 110。

所以,伽马率是二进制数 10110,即十进制中的 22。

epsilon率的计算方式类似;不是使用出现最多的位,而是使用每个位置出现最少的位。因此,epsilon率是 01001,即十进制中的 9。将伽马率 (22) 乘以epsilon率 (9) 得到功耗 198。

使用诊断报告中的二进制数字计算伽马率和epsilon率,然后将它们相乘。潜水艇的功耗是多少?(请确保以十进制表示你的答案,而不是二进制。)

我的解答,正式数据要把其中的5换成12

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,substr(a,1,1)b from a
union all
select lv+1,a,substr(a,lv+1,1) from b where lv<5),
c as( select lv,sum(b::int)s,count(*)c from b group by lv order by 1)
select sum((1<<(5-lv)) *case when s >c-s then 1 else 0 end) a1,
--sum((1<<(5-lv)) *case when s >c-s then 0 else 1 end )a2 
(1<<5) -a1-1 as a2, a1*a2
from c;

运算结果和题目给出的相同。

.read 2103.txt
┌────────┬────────┬───────────┐
│   a1   │   a2   │ (a1 * a2) │
│ int128 │ int128 │  int128   │
├────────┼────────┼───────────┤
│   22   │   9    │    198    │
└────────┴────────┴───────────┘

思路是用递归CTE将各层对应各列子字符串存入表中,然后对各层分组求各列之和,就得到了各列1的个数,将它和总行数相减,就是0的个数。最后根据层次倒序乘以2的幂就从二进制转成了十进制。出现最多值和最少值的总个数正好是2的n次幂-1,所以,第二个值可以简化计算。
本来还想优化,结果正式数据只要0.04秒就算出了结果,就不必优化了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值