DeepSeek对利用DuckDB求解Advent of Code 2021第9题“烟雾盆地”第二部分SQL的分析


这是DBatUTuebingen发布的。

源地址:https://github.com/DBatUTuebingen/Advent_of_Code


Advent of Code 2021 第9天

烟雾盆地

输入解析和低点计算放在共享文件 smoke-basin.sql 中(通过 .read 引入),两部分都会用到。

第一部分

用法:

复制代码
$ duckdb < smoke-basin-part1.sql
┌────────────┐
│ risk level │
│   int128   │
├────────────┤
│        526 │
└────────────┘
运行时间(秒):实际 0.000 用户 0.000190 系统 0.000082
第二部分

递归CTE flows 本质上计算了二维洞穴网格上的连通分量(分量由高度为 9 的网格点分隔)。

关键优化 :仅从第一部分找到的低点开始搜索连通分量(而不是 从所有网格点开始)。参见递归CTE flows 的初始查询 q₀cavelowpoints 的半连接。

用法:

在我的Mac Book Pro M2上大约需要30秒。

复制代码
$ duckdb < smoke-basin-part2.sql
┌─────────┐
│  sizes  │
│  int32  │
├─────────┤
│ 1123524 │
└─────────┘
运行时间(秒):实际 30.636 用户 23.693674 系统 6.924345

sql 复制代码
-- AoC 2021, Day 9 (Part 2)

-- AoC 输入文件
DROP MACRO IF EXISTS input;
CREATE MACRO input() AS 'input.txt';

-- 引入共享的SQL文件,包含高度图解析和低点计算
.read smoke-basin.sql

-- 开启计时器,并设置单线程运行(确保结果确定性)
.timer on
SET threads = 1;

WITH RECURSIVE
-- 1. 为每个网格点分配一个唯一的盆地编号(basin),初始为行号
cave(x, y, height, basin) AS (
  SELECT h.x, h.y, h.height, ROW_NUMBER() OVER () AS basin
  FROM   heightmap AS h
),
-- 2. 递归CTE:模拟水流扩散,确定每个点属于哪个盆地
flows(x, y, basin) AS (
  -- 初始查询:仅从低点开始扩散(优化关键)
  SELECT c.x, c.y, c.basin
  FROM   cave AS c SEMI JOIN lowpoints AS lp
         ON (c.y, c.x) = (lp.y, lp.x)
  
  UNION ALL  -- 递归部分:从已访问点向四个相邻点扩散
  
  SELECT c.x, c.y, LEAST(f.basin, c.basin) AS basin
  FROM   flows AS f, cave AS c
  WHERE  (c.x, c.y) IN ((f.x+1, f.y),
                        (f.x-1, f.y),
                        (f.x  , f.y+1),
                        (f.x  , f.y-1))
  AND    c.height < 9  -- 只扩散到高度小于9的点(高度9为边界)
),
-- 3. 确定每个点的最终盆地编号(取扩散过程中遇到的最小编号)
basins(x, y, basin) AS (
  SELECT f.x, f.y, MIN(f.basin) AS basin
  FROM   flows AS f
  GROUP BY f.y, f.x
)
-- 4. 计算前三大盆地大小的乘积
SELECT PRODUCT(b.size) :: int AS sizes
FROM   (SELECT COUNT(*) AS size
        FROM   basins AS b
        GROUP BY b.basin        -- 按盆地分组统计大小
        ORDER BY size DESC      -- 按大小降序排序
        LIMIT 3) AS b;          -- 取最大的三个盆地

SQL代码分析注释

  1. cave CTE

    • 为每个网格点 (x, y) 分配一个唯一的初始盆地编号 basin(使用 ROW_NUMBER())。
    • 这为后续的盆地合并提供了初始标识。
  2. flows 递归CTE

    • 初始查询 :只从低点(lowpoints)开始扩散,这是性能优化的关键,避免从所有点开始搜索。
    • 递归查询 :从已访问点向四个方向(上、下、左、右)扩散,但只扩散到高度 < 9 的点(因为高度9是盆地边界)。
    • LEAST(f.basin, c.basin):在扩散过程中,始终保留遇到的最小盆地编号,确保同一盆地的所有点最终有相同编号。
  3. basins CTE

    • flows 的结果按点 (x, y) 分组,取最小的 basin 编号作为该点的最终盆地归属。
  4. 最终查询

    • 统计每个盆地的大小(点数)。
    • 按大小降序排序,取前三大的盆地。
    • 使用 PRODUCT() 函数计算这三个大小的乘积。
    • 结果转换为 int 类型输出。

算法思路总结

该SQL通过递归CTE实现了基于低点的洪水填充算法(Flood Fill)

  • 从每个低点开始,向四周扩散直到遇到高度9的边界。
  • 扩散过程中合并盆地编号,确保同一盆地使用最小编号。
  • 最后统计各盆地大小,计算最大三个的乘积。

优化点在于仅从低点开始扩散,避免了从所有点开始的不必要计算,大幅提升了性能。

相关推荐
草莓熊Lotso2 小时前
技术深耕,破局成长:我的2025年度技术创作之路
大数据·开发语言·c++·人工智能·年度总结
小龙2 小时前
【学习笔记】通过准确率/精确率/召回率/F1分数判断模型效果+数据可视化实操
人工智能·笔记·学习·评价指标·大模型指标
mubei-1232 小时前
Retrieval-Augmented Generation(RAG) 开山之作:知识密集型NLP任务的检索增强生成
人工智能·深度学习·llm·rag·检索增强生成
NocoBase2 小时前
GitHub Star 数量前 12 的 AI 工作流项目
人工智能·低代码·开源·github·无代码
小杨互联网2 小时前
PyTorch分布式训练实战:从零构建Llama模型多GPU训练系统
人工智能·pytorch·llama
gjc5922 小时前
MySQL无主键大表删除导致主从同步延迟的深度分析
数据库·mysql
小鸡吃米…2 小时前
机器学习——基本概念
人工智能·机器学习
汪不止2 小时前
【 分布式唯一业务单号生成方案:Redis + 数据库双保险架构】
数据库·redis·分布式
典孝赢麻崩乐急2 小时前
Redis复习-------Redis事务
数据库·redis·缓存