前面用python实现了逻辑,
把迭代填充唯一可选数逻辑改写成一句postgresql格式SQL,输入表sudoku包括id,puzzle,输出包括id,puzzle,result,unkown列,如果已解决,unkown列为0,否则为剩余0的个数
sql
WITH RECURSIVE sudoku_solver AS (
SELECT
id,
puzzle,
puzzle as current_state,
0 as iteration
FROM sudoku
UNION ALL
SELECT
s.id,
s.puzzle,
new_state,
s.iteration + 1
FROM sudoku_solver s
CROSS JOIN LATERAL (
-- 计算当前状态的候选数和唯一候选数
WITH current_chars AS (
SELECT generate_series(1, 81) as pos,
substr(s.current_state, generate_series(1, 81), 1) as ch
),
candidates AS (
SELECT
cc.pos,
array_agg(n.n ORDER BY n.n) as cand_array
FROM current_chars cc
CROSS JOIN generate_series(1, 9) n(n)
WHERE cc.ch = '0'
AND NOT EXISTS (
SELECT 1
FROM current_chars cc2
WHERE ((cc.pos-1)/9) = ((cc2.pos-1)/9)
AND cc2.ch = n.n::text
UNION ALL
SELECT 1
FROM current_chars cc2
WHERE ((cc.pos-1)%9) = ((cc2.pos-1)%9)
AND cc2.ch = n.n::text
UNION ALL
SELECT 1
FROM current_chars cc2
WHERE
((cc.pos-1)/27) = ((cc2.pos-1)/27)
AND (((cc.pos-1)%9)/3) = (((cc2.pos-1)%9)/3)
AND cc2.ch = n.n::text
)
GROUP BY cc.pos
),
unique_candidates AS (
SELECT pos, cand_array[1] as val
FROM candidates
WHERE array_length(cand_array, 1) = 1
)
SELECT
string_agg(
CASE
WHEN uc.pos IS NOT NULL THEN uc.val::text
ELSE substr(s.current_state, p.pos, 1)
END,
'' ORDER BY p.pos
) as new_state,
COUNT(uc.pos) > 0 as has_unique -- 检查本轮是否有唯一候选数
FROM generate_series(1, 81) p(pos)
LEFT JOIN unique_candidates uc ON uc.pos = p.pos
) calc
WHERE
s.current_state LIKE '%0%'
AND s.iteration < 10
AND calc.has_unique -- 只有本轮有唯一候选数才继续
AND calc.new_state != s.current_state -- 确保有变化
)
SELECT
id,
puzzle,
current_state as result,
length(regexp_replace(current_state, '[^0]', '', 'g')) as unknown,
iteration
FROM (
SELECT
id,
puzzle,
current_state,
iteration,
row_number() OVER (PARTITION BY id ORDER BY iteration DESC) as rn
FROM sudoku_solver
) ranked
WHERE rn = 1
ORDER BY id;