把二进制位方法用于SQLite版本有一个问题需要解决,就是用字符串代替数组存储二进制状态,我在DuckDB版本基础上,把list函数改为GROUP_CONCAT,然后将语句和如下提示发给DeepSeek、
原SQLite版本计算初始化二进制状态有错,改成如下思路,并且不用逗号分隔,用定长3位十进制字符,更新时只需要拼接,不用replace。
他给出了如下的代码
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..')
),
digits(z, lp, bit) AS (
VALUES('1', 1, 1)
UNION ALL SELECT
CAST(lp+1 AS TEXT), lp+1, bit * 2 FROM digits WHERE lp<9
),
-- 初始化行列宫的二进制状态(基于已有数字)
init_state AS (
SELECT
sud,
(
select GROUP_CONCAT(s ,'') from(select substr(1000+sum(bit),2)s,rn from (select bit,value/9 rn from digits, generate_series(0,80) where substr(sud,value+1,1)=z union all select 0,value from generate_series(0,8)) group by rn order by rn)
) as rows,
(
select GROUP_CONCAT(s ,'') from(select substr(1000+sum(bit),2)s,rn from (select bit,value%9 rn from digits, generate_series(0,80) where substr(sud,value+1,1)=z union all select 0,value from generate_series(0,8)) group by rn order by rn)
) as cols,
(
select GROUP_CONCAT(s ,'') from(select substr(1000+sum(bit),2)s,rn from (select bit,(value/27*3 + value%9/3) rn from digits, generate_series(0,80) where substr(sud,value+1,1)=z union all select 0,value from generate_series(0,8)) group by rn order by rn)
) as boxes
FROM input
),
-- 递归求解
x(s, ind, rows, cols, boxes) AS (
SELECT
sud,
instr(sud, '.'),
rows,
cols,
boxes
FROM init_state
UNION ALL
SELECT
substr(s, 1, ind-1) || z || substr(s, ind+1),
instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' ),
-- 更新行的二进制状态
substr(rows, 1, ((ind-1)/9)*3) ||
substr(1000 + (CAST(substr(rows, ((ind-1)/9)*3+1, 3) AS INTEGER) | z.bit), 2) ||
substr(rows, ((ind-1)/9)*3+4),
-- 更新列的二进制状态
substr(cols, 1, ((ind-1)%9)*3) ||
substr(1000 + (CAST(substr(cols, ((ind-1)%9)*3+1, 3) AS INTEGER) | z.bit), 2) ||
substr(cols, ((ind-1)%9)*3+4),
-- 更新宫的二进制状态
substr(boxes, 1, (((ind-1)/27)*3 + ((ind-1)%9)/3)*3) ||
substr(1000 + (CAST(substr(boxes, (((ind-1)/27)*3 + ((ind-1)%9)/3)*3+1, 3) AS INTEGER) | z.bit), 2) ||
substr(boxes, (((ind-1)/27)*3 + ((ind-1)%9)/3)*3+4)
FROM x, digits AS z
WHERE ind>0
AND z.lp BETWEEN 1 AND 9
-- 使用位运算检查是否可以放置数字
AND (CAST(substr(rows, ((ind-1)/9)*3+1, 3) AS INTEGER) & z.bit) = 0
AND (CAST(substr(cols, ((ind-1)%9)*3+1, 3) AS INTEGER) & z.bit) = 0
AND (CAST(substr(boxes, (((ind-1)/27)*3 + ((ind-1)%9)/3)*3+1, 3) AS INTEGER) & z.bit) = 0
)
SELECT s FROM x WHERE ind=0;
它计算原有的例子没问题。但是我把只有17个已知数的最小数独给它,却算不出来,经过调试发现,当某行全为空时,它不返回sum值,导致二进制状态缺行,再次让DeepSeek修改,他给出的解决方法是COALESCE或左连接,但实际上不能用,还是手工把它改成union all 一个行号0值序列,再做分组,此思路已改在上述代码,现在就能处理最小数独了。
把上述代码保存为 dssql2.txt,将它与原始SQLite示例比较,结果如下
sql
-- 原有例子
sqlite> .read dssql2.txt
┌──────────────────────────────────────────────────────────────┐
│ s │
├──────────────────────────────────────────────────────────────┤
│ 346251897759863124821974365195486273683712459472539681238197 │
│ 546914625738567348912 │
└──────────────────────────────────────────────────────────────┘
Run Time: real 3.376290 user 3.344000 sys 0.032000
sqlite> .read sqlsudoku.txt
┌──────────────────────────────────────────────────────────────┐
│ s │
├──────────────────────────────────────────────────────────────┤
│ 346251897759863124821974365195486273683712459472539681238197 │
│ 546914625738567348912 │
└──────────────────────────────────────────────────────────────┘
Run Time: real 10.713583 user 10.660000 sys 0.028000
-- 17个已知数的最小数独
sqlite> .read sqlsudoku.txt
┌──────────────────────────────────────────────────────────────┐
│ s │
├──────────────────────────────────────────────────────────────┤
│ 932567841157824963648319725576982134893146572421753689714298 │
│ 356285631497369475218 │
└──────────────────────────────────────────────────────────────┘
Run Time: real 72.085067 user 71.812000 sys 0.264000
sqlite> .read dssql2.txt
┌──────────────────────────────────────────────────────────────┐
│ s │
├──────────────────────────────────────────────────────────────┤
│ 932567841157824963648319725576982134893146572421753689714298 │
│ 356285631497369475218 │
└──────────────────────────────────────────────────────────────┘
Run Time: real 21.870196 user 21.292000 sys 0.388000
可见,尽管需要把数字存在字符串,二进制位版本还是比原始版本快了3倍。