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

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

相关推荐
NAGNIP7 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab9 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab9 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP12 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年12 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼13 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS13 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区14 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈14 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang14 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx