news 2026/4/15 21:39:33

DeepSeek总结的SQL 数独:约束编程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepSeek总结的SQL 数独:约束编程

原文: SQL Sudoku Constraint Programming

#1
SQL 数独:约束编程
CM Lubinski

考虑数独游戏,最常在九乘九的单元格网格上进行,其中每个单元格可以包含1到9的整数之一。游戏规定每一行必须只包含互不相同的元素,每一列以及九个三乘三的子棋盘也必须如此。具体的游戏开局时,棋盘上已有一些单元格被预先填入数字,玩家需要推导出剩余的值。

实际上,我们可以比"推导"更具体;我们玩游戏时,很可能是在减少每个单元格可能的选择数量。我们认识到每条规则——例如行包含互不相同的值等——限制了它所影响的单元格的取值。赢得游戏等同于找到一组不违反任何规则的单元格取值。我们难道不能简单地定义规则,然后让计算机来搜索吗?它肯定会实现比我们使用的任何策略都更高效的方法。

这正是约束编程(一种声明式编程)所承诺的。为了进一步介绍它,让我们看看其中一个更常见的实现:SQL。

SQL 数独

通过在 SQL 中实现数独,我们将看到一些在整个约束编程中反复出现的高级概念。我们还会遇到 SQL 实现中的几个痛点,这些问题可以通过使用更通用的语言(如稍后介绍的 Minizinc)来缓解。

让我们首先为每个单元格创建一个值域;这是任意单元格可能包含的所有可能值的集合(1-9)。

CREATETABLEcell_domain(vint);INSERTINTOcell_domainVALUES(1);INSERTINTOcell_domainVALUES(2);INSERTINTOcell_domainVALUES(3);INSERTINTOcell_domainVALUES(4);INSERTINTOcell_domainVALUES(5);INSERTINTOcell_domainVALUES(6);INSERTINTOcell_domainVALUES(7);INSERTINTOcell_domainVALUES(8);INSERTINTOcell_domainVALUES(9);

一行由九个这样的值组成,要求每个单元格的值互不相同。我实在想不出比逐个比较每对值(我们很快会再次遇到这种冗长问题)更优雅的方式来编码这种必要的差异性。和单元格类似,我们正在定义数独一行的值域。每个 SQL 条目代表一个可能的数独行。

CREATETABLEsudokurow(c1int,c2int,c3int,c4int,c5int,c6int,c7int,c8int,c9int);INSERTINTOsudokurowSELECTd1.vasc1,d2.vasc2,...d9.vasc9-- 每个单元格FROMcell_domainasd1,cell_domainasd2,...WHEREc1<>c2andc1<>c3and...-- c1 是唯一的andc2<>c3andc2<>c4and...-- c2 是唯一的...andc8<>c9-- c8 和 c9 是唯一的;

现在我们有了行,就可以实现一个数独棋盘的表示。由于可能的数独棋盘数量太多(即值域太大),无法具体化,我们将使用一个 SQL 视图来表示数独棋盘的值域。同样,我们将使用成对的差异性约束,这意味着大量的不等式。

CREATEVIEWboardASSELECTr1.c1asc11,r1.c2asc12,......r9.c8asc98,r9.c9asc99FROMsudokurowasr1,sudokurowasr2,...WHERE-- 列值互异c11<>c21andc11<>c31...andc21<>c31andc21<>c41......c19<>c29andc19<>c39...andc29<>c39andc29<>c49...-- 子方块值互异(全部九个)andc11<>c22andc11<>c23......;

求解一个特定的数独棋盘就变得和在 “where” 子句中包含初始配置的 select 查询一样简单:

SELECT*FROMboardWHEREc11=1andc22=2and...;

在食谱中加入 Zinc

我进行约束编程的首选工具是 Zinc 套件。Minizinc 是一种用于描述约束问题的高级开源语言;其源文件被编译成一种中立但较低级的语言,称为 Flatzinc。然后,Flatzinc 文件可以被众多约束求解器之一读取和求解,因为存在许多潜在的搜索实现方式。打个比方,Minizinc 是源代码,Flatzinc 是虚拟机字节码,而求解器是特定于操作系统的运行时环境。

让我们看看数独如何适应 Minizinc。我们从数独棋盘开始,它由 9x9 个单元格组成,每个单元格的取值范围在整数 1 到 9 之间。

array[1..9, 1..9] of var 1..9: board;

接下来,我们添加约束来表示对行和列的限制。注意这比 SQL 实现简洁多少。

constraint forall (row in 1..9) (alldifferent (col in 1..9) (board[row, col])); constraint forall (col in 1..9) (alldifferent (row in 1..9) (board[row, col]));

我们还为每个包含互异值的子网格添加约束。下面的描述为了清晰而过于显式——我们可以很容易地使用 for 循环来达到同样的效果。

constraint ( alldifferent (row in 1..3, col in 1..3) (board[row, col]) /\ alldifferent (row in 1..3, col in 4..6) (board[row, col]) /\ alldifferent (row in 1..3, col in 7..9) (board[row, col]) /\ alldifferent (row in 4..6, col in 1..3) (board[row, col]) /\ alldifferent (row in 4..6, col in 4..6) (board[row, col]) /\ alldifferent (row in 4..6, col in 7..9) (board[row, col]) /\ alldifferent (row in 7..9, col in 1..3) (board[row, col]) /\ alldifferent (row in 7..9, col in 4..6) (board[row, col]) /\ alldifferent (row in 7..9, col in 7..9) (board[row, col]) );

最后,我们需要为棋盘的初始配置(即我们给定的值)添加一个约束:

constraint ( board[1, 1] = 1 /\ board[2, 2] = 2 /\ ... );

去芜存菁

如果你在计算机科学理论上花过任何时间,很可能遇到过 NP 难问题,即其复杂度随输入规模呈指数级(或更糟)增长的任务。不幸的是,这些问题在"现实世界"中非常普遍。你可能需要为共享资源制定最佳时间表,找到两个文档之间的最小必要更改,或者甚至只是解决像上面数独游戏这样的问题。这些问题的变体都是 NP 难的,因此对于大型输入,仅仅检查每个可能的答案并挑选最优的是不可行的。

幸运的是,我们并不总是需要检查所有可能的解来找到最优解。例如,在数独中,许多棋盘选择会产生无效配置——即每个方块、列或行中的数字不唯一。使用约束进行编程意味着将搜索空间限制在那些有效的解决方案上,从而显著减少运行时间。

然而,约束编程环境和库提供的不仅仅是对限制搜索空间的良好隐喻。它们通常还提供非常高效的引擎来搜索可能性空间。考虑我们的数独游戏;每个单元格可以取值的范围取决于同一行、同一列和同一方块中已经已知的单元格。了解一个单元格的潜在取值范围反过来又对其他单元格提供了额外的约束。例如,如果一行中除一个单元格外的所有单元格的取值范围都排除了数字 8,那么我们可以肯定那个例外单元格包含数字 8,无论其取值范围中还有其他什么值。在调整取值范围时的这种来回过程被称为"传播"。

经过足够的传播后,系统会意识到它无法再进一步缩小搜索空间,程序将需要进行猜测。然后,这个猜测会触发新一轮的传播。它会跟踪每个"决策",以便能够撤销它所做的一系列猜测并尝试不同的选择,始终寻求最优解。决策最终可能导致无效或"失败"的配置,这指示何时需要撤销决策堆栈,并且某些搜索策略(例如二分搜索)可能以更高效的顺序进行"猜测"。

请注意,解决方案既是正确的也是最优的。我们只是通过丢弃无效配置来节省工作量(从而节省时间),而不是通过丢弃有效解。如果我们没有提供足够的约束,搜索空间将仍然巨大得无法计算。我们描述的约束越多,需要执行的工作就越少,我们找到解决方案的速度也就越快。你也可以想象,如果我们将搜索空间限制得比问题的要求更严格,我们可以快速地找到次优解。

实现说明

要在你的应用程序中使用 Minizinc,很可能需要你的应用程序编写一个 Minizinc 源文件。然后它可以调用一个一次性的编译器/执行器并读取结果,这些结果通常以 JSON 形式读取。这种方法在开发期间特别有吸引力,因为直接访问 minizinc 输入文件和编译器将使调试更简单。对于生产应用程序,你可能希望切换到较低级别的库(例如 Gecode)以获得性能提升,但 Minizinc 对于开发和学习约束编程基础知识非常棒。实际上有一个 Flatzinc 的 Gecode 实现,这意味着你只会受到编译开销的影响。

资源

  • SQL 数独初始化脚本
  • 完整的 Minizinc 数独脚本
  • Minizinc: 官网, 教程
  • Coursera 的离散优化课程 (来自 Pascal Van Hentenryck)
  • Gecode 的 flatzinc 插件
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 22:16:23

C#上位机大数据量处理:异步采集+多线程解析

异步采集实现方法使用C#的async/await语法配合Task类实现非阻塞数据采集。通过HttpClient或串口通信库的异步方法进行数据获取&#xff0c;避免主线程被阻塞。private async Task<List<byte[]>> CollectDataAsync(string deviceUrl) {var results new List<byt…

作者头像 李华
网站建设 2026/4/16 12:31:46

这样做的幂等也太全了吧!

在做票务下单的时候&#xff0c;肯定要做幂等和放重复的&#xff0c;防止用户操作出现重复的订单和重复支付等问题&#xff0c;于是有了本篇文章。幂等设计需分层防护&#xff0c;从接口层到数据层形成完整防线。推荐以下方案&#xff1a;1. 接口层&#xff1a;幂等Token机制&a…

作者头像 李华
网站建设 2026/3/25 18:22:38

CentOS7安装Redis6全攻略

一、介绍 Redis&#xff08;Remote Dictionary Server&#xff09;是一款基于内存的高性能键值对存储数据库&#xff0c;它以极快的读写速度和丰富的数据结构&#xff0c;成为了众多开发者解决高并发、低延迟问题的首选方案。CentOS是Red Hat Enterprise Linux&#xff08;RHE…

作者头像 李华
网站建设 2026/4/16 10:54:03

基于腾讯元器搭建智能体“看图写诗词专家”Agent智能体搭建笔记

本文系统梳理基于腾讯元器平台构建“看图写诗词专家”智能体的全流程实操要点&#xff0c;涵盖前期需求锚定、核心功能搭建、Multi_Agent关系配置、测试优化及运维保障等关键环节。该智能体采用Multi_Agent模式开发&#xff0c;核心定位为“图文意境适配的诗词创作智能助手”&a…

作者头像 李华
网站建设 2026/4/16 10:16:25

有实力的金包银有哪些

金包银行业深度剖析&#xff1a;六六珠宝脱颖而出行业痛点分析在金包银领域&#xff0c;当前存在着诸多技术挑战。其中&#xff0c;金层厚度不均、结合力不足以及耐磨性差是较为突出的问题。测试显示&#xff0c;市场上部分金包银产品的金层厚度偏差可达 20%以上&#xff0c;这…

作者头像 李华
网站建设 2026/4/16 12:35:50

AI智能体在识别价值陷阱和价值机会中的作用

AI智能体在识别价值陷阱和价值机会中的作用 关键词:AI智能体、价值陷阱、价值机会、数据分析、金融市场 摘要:本文深入探讨了AI智能体在识别价值陷阱和价值机会方面的作用。首先介绍了相关背景知识,包括研究目的、预期读者、文档结构和术语表。接着阐述了核心概念,如AI智能…

作者头像 李华