利用短整数类型和部分字符串优化DuckDB利用数组求解数独SQL

与利用定长字符串版本相比,利用整数数组的速度略慢,这是因为数组是复杂数据结构,有开销,这个开销抵消了字符串转整数的开销。

因此,它还有几处细节可以优化。

1.存储二进制位状态的rows, cols, boxes数组都保存了大整数,而在每个递归步骤都要保存当前的状态,多用了几倍的空间,考虑每个二进制不超过512,可以用转成smallintl类型来优化。

实测计算只有17个已知数的最小数独,定长字符串版本用时3.048秒,大整数数组用时4.686 秒,短整数数组用时3.212秒。

2.每步用instr从字符串找最前.位置不必要,其实每步的.位置都是确定的, 可以在第一步计算完毕,供以后读取。

3.基于第二点,每步保存的字符串不必是完整字符串,只要保存当前状态部分就够了,然后在最后一步,将它与input再合并成完整字符串。使用了这个优化,短整数数组用时2.881秒,终于反超了定长字符串版本。

sql 复制代码
WITH RECURSIVE
  input(sud) AS (
 VALUES('9......4..5..2........1......69..1........5.24..7......1....3.....6...9....4.....')
--    VALUES('.....1..7....6..2.8..9..3...954....3..3...4..4......8......7..6.1..2....5..3..9..')
  ),
  a(ins) as(select list(i::tinyint order by i) from input, range(1, 82)t(i) where sud[i]='.'), 
  digits(z, lp, bit) AS (
    VALUES('1', 1, 1::smallint)
    UNION ALL SELECT 
    CAST(lp+1 AS TEXT), lp+1, bit * 2 FROM digits WHERE lp<9
  ),
  -- 初始化行列宫的二进制状态(基于已有数字)
  init_state AS (
    SELECT 
      sud,(select list(s::smallint order by rn) from(select sum(bit)s,rn from(select bit,i//9 rn from digits, range(0,81) t(i) where substr(sud,i+1,1)=z union all select 0,i from range(9)t(i)) group by rn)) as rows,

      -- 列的二进制状态数组
      (select list(s::smallint order by rn) from(select sum(bit)s,rn from(select bit,i%9 rn from digits, range(0,81) t(i) where substr(sud,i+1,1)=z union all select 0,i from range(9)t(i)) group by rn)) as cols,

      -- 宫的二进制状态数组
      (select list(s::smallint order by rn) from(select sum(bit)s,rn from(select bit,(i//27*3 + i%9//3) rn from digits, range(0,81) t(i) where substr(sud,i+1,1)=z union all select 0,i from range(9)t(i)) group by rn)) as boxes
    FROM input
  ),
  -- 递归求解
  x(lv, s, ind, rows, cols, boxes) AS (
    SELECT 1::tinyint, '', 
      --substr(replace(sud,'.',''),1,ins[1]-1) 
      ins[1], 
      --instr(sud, '.'),
      rows,
      cols,
      boxes
    FROM init_state, a
    UNION ALL
    SELECT lv+1, 
      s||z, 
      --substr(s, 1, ind-1) || z || substr(s, ind+1),
      ins[lv+1], 
      --instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' ),
      -- 更新行的二进制状态
      list_transform(rows, (val, idx) -> 
        CASE WHEN idx = ((ind-1)//9) + 1 THEN val | z.bit ELSE val END),
      -- 更新列的二进制状态
      list_transform(cols, (val, idx) -> 
        CASE WHEN idx = ((ind-1)%9) + 1 THEN val | z.bit ELSE val END),
      -- 更新宫的二进制状态
      list_transform(boxes, (val, idx) -> 
        CASE WHEN idx = (((ind-1)//27)*3 + ((ind-1)%9)//3) + 1 THEN val | z.bit ELSE val END)
    FROM x, digits AS z, a
    WHERE ind>0
      -- AND z.lp BETWEEN 1 AND 9
      -- 使用位运算检查是否可以放置数字
      AND (rows[((ind-1)//9) + 1] & z.bit) = 0
      AND (cols[((ind-1)%9) + 1] & z.bit) = 0
      AND (boxes[(((ind-1)//27)*3 + ((ind-1)%9)//3) + 1] & z.bit) = 0
 )--select s from x,a where lv=len(ins)+1;
  , j as(select s from x,a where lv=len(ins)+1) --结果部分字符串
 , j2 as(select substr(s, i, 1) s, ins[i] idx from j, a , range(1, 82)t(i) where i <=len(ins)) --带位置的结果字符表
 , j3 as(select substr(sud, i, 1)s, i idx from input , range(1, 82)t(i) where substr(sud, i, 1)<>'.') --带位置的初始字符表
 select listagg(s, '' order by idx) from (from j2 union all from j3);
相关推荐
꧁细听勿语情꧂5 分钟前
二叉树余题,排序、直接插入排序、希尔排序
c语言·开发语言·数据结构·算法
是main不是漫31 分钟前
【C++——类和对象(上)】筑基篇:以封装奠定面向对象之基
c语言·开发语言·c++
唐老板36 分钟前
uuidv7 来了:为什么别再拿 uuidv4 当主键了
数据库
apcipot_rain4 小时前
C++转Java日记(06)
java·开发语言
SomeOtherTime11 小时前
Rust所有权(AI回答)
开发语言·后端·rust
Easyhood11 小时前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
白杨尚青12 小时前
C++进阶篇(六):map 和 set 的模拟实现——用红黑树封装 mymap/myset(迭代器 + operator[])
开发语言·c++·迭代器·stl·set·map·红黑树
SelectDB14 小时前
让变更流出 Doris:Apache Doris 5.0 基于增量计算的 CDC 技术解析
数据库
这个DBA有点耶14 小时前
MCP隔离层+行级语义审计+输出校验:AI访问数据库的安全边界拆解
数据库·人工智能·ai编程
新*15 小时前
Elasticsearch 7.17 的 SQL 到底能查到什么?一次踩坑记录
后端·sql·elasticsearch·查询