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

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

相关推荐
IT实战课堂小元酱几秒前
大数据深度学习|计算机毕设项目|计算机毕设答辩|flask露天矿爆破效果分析系统开发及应用
人工智能·python·flask
2501_927993532 分钟前
SQL Server 2022安装详细教程(图文详解,非常详细)
数据库·sqlserver
星火s漫天3 分钟前
第一篇: 使用Docker部署flask项目(Flask + DB 容器化)
数据库·docker·flask
xcLeigh8 分钟前
Python 项目实战:用 Flask 实现 MySQL 数据库增删改查 API
数据库·python·mysql·flask·教程·python3
威迪斯特9 分钟前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
xu_yule11 分钟前
Redis存储(15)Redis的应用_分布式锁_Lua脚本/Redlock算法
数据库·redis·分布式
MSTcheng.19 分钟前
CANN ops-math:AI 硬件端高效数学运算的算子设计与工程化落地方法
人工智能·深度学习·cann
Dev7z24 分钟前
基于深度学习的肺部听诊音疾病智能诊断方法研究
人工智能·深度学习
一灰灰blog27 分钟前
Spring AI中的多轮对话艺术:让大模型主动提问获取明确需求
数据库·人工智能·spring
行者无疆_ty34 分钟前
什么是Node.js,跟OpenCode/OpenClaw有什么关系?
人工智能·node.js·openclaw