利用DeepSeek辅助翻译clickhouse SQL为DuckDB 格式求解Advent of Code 2025第10题 电子工厂 第二部分

首先把clickhouse 解题 SQL发给DeepSeek,

用中文解读附件clickhouse格式sql的解题逻辑,特别是part2的逻辑

它给出了以下解读。

看了这个ClickHouse SQL,这是解决Advent of Code(AOC)第10天的题目,主要涉及按钮组合和状态转换的优化问题。让我详细解读逻辑:

整体问题结构

题目包含两部分:

  1. Part 1:找到最少按钮次数,使LED灯(用0/1表示)变成目标图案
  2. Part 2:扩展问题,考虑不同的"电压"目标,需要计算最小总成本

Part 1 逻辑(暴力枚举)

sql 复制代码
-- 核心思路:枚举所有按钮组合(2^n种可能)
-- 每个按钮影响特定位置,按下改变对应位置的状态(XOR运算)
-- 找到与目标图案完全匹配且按钮次数最少的结果
  1. 解析输入:将'#'转为1,'.'转为0
  2. 枚举组合:对n个按钮,生成0到2^n-1的所有位掩码
  3. 计算效果:对每个组合,计算每个位置被按下的按钮数之和,取模2(XOR)
  4. 匹配目标:筛选出resulting_diagram = target_diagram的组合
  5. 找最小:按bitCount(combination_id)排序,取最小值

Part 2 核心逻辑(难点)

这是问题的核心创新点,采用了递归折半算法

1. 预计算所有按钮组合的"模式"

sql 复制代码
-- 为每个按钮组合计算:
-- effect_pattern: 每个位置被按下的次数(整数,不只是0/1)
-- parity_pattern: 每个位置的奇偶性(模2的结果)
-- pattern_cost: 该组合的按钮次数

2. 按奇偶性分组模式

sql 复制代码
-- 将effect_pattern按parity_pattern分组
-- 相同奇偶约束的模式放在一起,便于后续查找

3. 关键算法:递归折半

sql 复制代码
-- 初始:current_goal = target_joltages(目标电压值)
-- 递归步骤:
--   1. 根据current_goal的奇偶性选择合适的模式组(parity_pattern必须匹配)
--   2. 应用一个模式:current_goal = current_goal - effect_pattern
--   3. 折半:current_goal = intDiv(current_goal, 2)
--   4. 累计成本:cost += pattern_cost * (2^depth)

算法原理理解

这实际上是一个基于二进制的动态规划

  • 为什么要匹配奇偶性?

    因为 (current - pattern) / 2 必须是整数,所以currentpattern在每个位置必须同奇偶

  • 为什么除以2?

    每次递归将问题规模减半,从二进制角度看:

    • 每个位置的电压值看成二进制数
    • 奇偶性 = 最低位
    • 除以2 = 右移一位(去掉已处理的最低位)
  • 成本计算为什么是pattern_cost * 2^depth

    假设最优解中某个模式需要在第d层(深度)使用k次

    • 在第d层用1次 → 成本 = pattern_cost
    • 在第d-1层用1次 → 相当于第d层用2次 → 成本 = pattern_cost * 2
    • 以此类推:成本 = pattern_cost × 2^depth

4. 终止条件

sql 复制代码
-- 当current_goal全为0时停止
-- 从所有路径中找最小accumulated_cost

算法复杂度分析

  • Part 1:O(2^n × m),n=按钮数,m=位置数(暴力搜索)
  • Part 2 :O(2^n + L × P × D),其中:
    • 2^n是预计算模式
    • L是递归深度(≤100)
    • P是模式数量
    • D是目标维度

实际应用思路

这个Part 2的算法很巧妙:

  1. 将多位置问题分解为独立但又相互关联的子问题
  2. 利用二进制表示和奇偶性约束大幅减少搜索空间
  3. 通过"除以2"操作将指数问题转化为线性递归
  4. 动态规划思想:从最低位开始逐位解决

这是一个典型的状态压缩+二进制优化的竞赛题目解法,展示了如何将看似复杂的组合优化问题转化为高效的数学计算问题。

看来他理解得不错,继续提示

利用下面已有的部分duckdb sql,将上述clickhouse sql计算part2的逻辑翻译为duckdb sql,不做别的

基于已有的DuckDB SQL结构和Part 2的逻辑,我翻译出以下完整代码:

sql 复制代码
WITH RECURSIVE t AS (
    SELECT 
    '[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
    [#...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
    [.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}' t
),
b AS (
    SELECT 
        row_number() OVER () rn,
        reverse(substr(b, 2, instr(b, ']') - 2)) b1, -- 信号灯字符串,格式.##.,注意要翻转字符串
        substr(b, instr(b, ']') + 2, instr(b, '{') - 3 - instr(b, ']')) b2, -- 按钮字符串,格式(3) (1,3) (2) (2,3) (0,2) (0,1)
        substr(b, instr(b, '{') + 1, instr(b, '}') - 1 - instr(b, '{')) b3, -- 伏特字符串,格式3,5,4,7
        translate(b1, '#.', '10')::BIT::INT b1a, -- 转二进制位再转整数
        list_reduce([0] || string_split(unnest(string_split(replace(replace(b2, ')', ''), '(', ''), ' ')), ',')::INT[], 
                   lambda x, y : (x + (1 << y))) b2a, -- 按钮转成整数列表
        string_split(b3, ',')::INT[] b3a
    FROM (SELECT unnest(string_split(t, chr(10))) b FROM t)
),
d AS (
    SELECT rn, b1a, array_agg(b2a) a, b3a 
    FROM b 
    GROUP BY all
),

-- PART 2: 预计算按钮组合模式
button_combination_patterns AS (
    SELECT
        rn as puzzle_id,
        a as button_effects,
        b3a as target_joltages,
        cardinality(a) as num_buttons,
        cardinality(b3a) as num_positions,
        combination_id,
        bit_count(combination_id) as pattern_cost,
        
        -- effect_pattern: 每个位置被按下的次数
        (
            SELECT list_agg(
                (
                    SELECT count(*) 
                    FROM unnest(a) as effect
                    WHERE bit_test(combination_id, index) 
                      AND bit_test(effect, pos-1)
                )
                ORDER BY pos
            )
            FROM generate_series(1, cardinality(b3a)) as pos
        )::INT[] as effect_pattern,
        
        -- parity_pattern: 每个位置的奇偶性 (模2)
        (
            SELECT list_agg(
                (
                    SELECT count(*) 
                    FROM unnest(a) as effect
                    WHERE bit_test(combination_id, index) 
                      AND bit_test(effect, pos-1)
                ) % 2
                ORDER BY pos
            )
            FROM generate_series(1, cardinality(b3a)) as pos
        )::INT[] as parity_pattern
    FROM d
    CROSS JOIN generate_series(0, (1 << cardinality(a)) - 1) as combination_id
    CROSS JOIN (SELECT generate_subscripts(a, 1) - 1 as index) as indexes
),

-- 按奇偶性分组模式
patterns_grouped_by_parity AS (
    SELECT
        puzzle_id,
        button_effects,
        num_buttons,
        num_positions,
        parity_pattern,
        list_agg((effect_pattern, pattern_cost)) as available_patterns
    FROM button_combination_patterns
    GROUP BY puzzle_id, button_effects, num_buttons, num_positions, parity_pattern
),

-- 递归折半算法
recursive_halving_solver AS (
    -- 基础情况: 从目标电压开始
    SELECT
        puzzle_id,
        button_effects,
        num_buttons,
        num_positions,
        target_joltages as current_goal,
        0::BIGINT as accumulated_cost,
        0 as recursion_depth
    FROM d
    JOIN button_combination_patterns USING (puzzle_id)
    GROUP BY puzzle_id, button_effects, num_buttons, num_positions, target_joltages
    
    UNION ALL
    
    -- 递归情况: 应用模式,减去,折半,继续
    SELECT DISTINCT
        solver.puzzle_id,
        solver.button_effects,
        solver.num_buttons,
        solver.num_positions,
        (
            SELECT list_agg(
                (solver.current_goal[pos] - pattern_tuple[1][pos]) / 2
                ORDER BY pos
            )
            FROM generate_series(1, solver.num_positions) as pos
        )::INT[] as current_goal,
        
        solver.accumulated_cost + 
            pattern_tuple[2]::BIGINT * (1 << solver.recursion_depth) as accumulated_cost,
        
        solver.recursion_depth + 1 as recursion_depth
    FROM recursive_halving_solver solver
    JOIN patterns_grouped_by_parity patterns
        ON patterns.puzzle_id = solver.puzzle_id
        AND patterns.parity_pattern = (
            SELECT list_agg(solver.current_goal[pos] % 2 ORDER BY pos)
            FROM generate_series(1, solver.num_positions) as pos
        )
    CROSS JOIN unnest(patterns.available_patterns) as pattern_tuple
    WHERE
        solver.recursion_depth < 100
        AND EXISTS (
            SELECT 1 
            FROM unnest(solver.current_goal) as val 
            WHERE val != 0
        )
        -- 确保模式不会超出当前目标
        AND NOT EXISTS (
            SELECT 1
            FROM generate_series(1, solver.num_positions) as pos
            WHERE pattern_tuple[1][pos] > solver.current_goal[pos]
        )
),

-- Part 2 最小成本解决方案
part2_minimum_solutions AS (
    SELECT
        puzzle_id,
        min(accumulated_cost) as minimum_cost
    FROM recursive_halving_solver
    WHERE NOT EXISTS (
        SELECT 1 
        FROM unnest(current_goal) as val 
        WHERE val != 0
    )
    GROUP BY puzzle_id
)

-- 输出Part 2结果
SELECT 
    'Part 2' as part,
    sum(minimum_cost) as solution
FROM part2_minimum_solutions;

关键翻译要点:

  1. 数据类型转换 :ClickHouse的UInt32转为DuckDB的INTUInt64转为BIGINT

  2. 数组操作

    • arrayMap → 使用list_agg配合子查询
    • arrayReduce → 使用count(*)sum()
    • arrayAll → 使用NOT EXISTSEXISTS
  3. 位运算

    • bitTestbit_test
    • bitCountbit_count
    • pow(2, n)(1 << n)
  4. 递归结构

    • 保持相同的递归折半算法逻辑
    • 使用DuckDB的RECURSIVE CTE语法
  5. 模式匹配

    • 奇偶性约束通过current_goal % 2计算
    • 模式选择基于奇偶性匹配
  6. 成本计算

    • pattern_cost * 2^depth 转为 pattern_tuple[2] * (1 << depth)

这个翻译保持了原ClickHouse SQL的逻辑完整性,只进行了必要的语法转换。

但以上SQL有错误,主要是以下方面

  1. duckdb没有list_agg函数,应该用list函数代替
  2. duckdb没有bit_test函数,应该用位运算代替
  3. duckdb没有cardinality函数,应该用length函数代替
  4. DuckDB中FROM generate_series(1, solver.num_positions) as pos的写法会把pos当作一个结构,而不是表的列,需要改成... as t(pos)
  5. button_combination_patterns子查询中的indexes应该放到相关子查询,而非主查询
  6. recursive_halving_solver子查询将递归部分的求min改成了DISTINCT, 造成中间结果过多。

我将以上部分都修改后,求解成功。

sql 复制代码
memory D .read 2510p2a.sql
┌─────────┬──────────┐
│  part   │ solution │
│ varchar │  int128  │
├─────────┼──────────┤
│ Part 2  │    19810 │
└─────────┴──────────┘
Run Time (s): real 142.785 user 348.915288 sys 73.074016

比同样机器上执行原始ClickHouse SQL慢了36秒。

相关推荐
zxsz_com_cn2 小时前
智能制造设备预测性维护解决方案详解
人工智能
DarkAthena2 小时前
【GaussDB】分析函数性能优化案例-row_number改写
数据库·sql·oracle·性能优化·gaussdb
techdashen2 小时前
借助gh-ost,对MySQL大表进行表结构的变更
数据库·mysql
AI_56782 小时前
K8s新手入门:从“Pod创建”到“服务暴露”,3个案例理解容器编排
人工智能·学习·测试工具
肥猪猪爸2 小时前
NLP中BIO标签浅析
人工智能·深度学习·神经网络·机器学习·自然语言处理·nlp
快降重科研小助手2 小时前
AI率单独优化:用“快降重”专项功能,能否安全绕过知网/维普AIGC检测?
人工智能·aigc·降ai率·论文降ai·快降重
咯哦哦哦哦2 小时前
image_to_world_plane 如何计算校正图像(rectified image)的尺寸、比例(Scale)、位姿(Pose)
人工智能·计算机视觉·3d
蓝海星梦2 小时前
【强化学习】深度解析 GSPO:解决 GRPO 中优化目标与奖励不匹配的问题
论文阅读·人工智能·自然语言处理·大语言模型·强化学习
Lethehong2 小时前
破局Oracle迁移困局:破局Oracle迁移困局:直面兼容性与成本的隐性痛点
数据库·oracle