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₀ 中 cave 和 lowpoints 的半连接。

用法:

在我的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的边界。
  • 扩散过程中合并盆地编号,确保同一盆地使用最小编号。
  • 最后统计各盆地大小,计算最大三个的乘积。

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

相关推荐
Java后端的Ai之路13 分钟前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
数字新视界20 分钟前
机房动环监控系统在提升数据中心能效与安全中的应用探索
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
Flynn Liang28 分钟前
Spring AI 上生产:限流熔断、密钥与成本治理、私有化/混合部署、降级与 Prompt 灰度架构全解
人工智能·spring·架构·高可用·springai·成本治理·生产架构
飞猫的边缘AI37 分钟前
边缘AI芯片的规则变了:从跑CNN到跑LLM和VLM
人工智能·语言模型·cnn·transformer·边缘ai
code2cat1 小时前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
bksczm1 小时前
MySQL之存储引擎
数据库·mysql
牧羊人.3331 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn
Flynn Liang1 小时前
Spring AI 2.0 深度解析:ChatClient、Advisor 链与 MCP——Java 工程师的 AI 落地最优解
java·人工智能·spring·大模型·架构设计·spring ai
Martina_03211 小时前
AI生成3D模型一合并网格,材质就串色?用6步检查材质槽、UV与图集重映射
图像处理·人工智能·3d·材质·uv·游戏策划·1024程序员节
蓝鲨硬科技1 小时前
Token之后,物理AI需要什么样的度量衡?
大数据·人工智能