仅利用30行Python代码来展示X算法


Posted in Python onApril 01, 2015

假如你对数独解法感兴趣,你可能听说过精确覆盖问题。给定全集 X 和 X 的子集的集合 Y ,存在一个 Y 的子集 Y*,使得 Y* 构成 X 的一种分割。

这儿有个Python写的例子。
 

X = {1, 2, 3, 4, 5, 6, 7}
Y = {
  'A': [1, 4, 7],
  'B': [1, 4],
  'C': [4, 5, 7],
  'D': [3, 5, 6],
  'E': [2, 3, 6, 7],
  'F': [2, 7]}

这个例子的唯一解是['B', 'D', 'F']。

精确覆盖问题是NP完备(译注:指没有任何一个够快的方法可以在合理的时间内,意即多项式时间 找到答案)。X算法是由大牛高德纳发明并实现。他提出了一种高效的实现技术叫舞蹈链,使用双向链表来表示该问题的矩阵。

然而,舞蹈链实现起来可能相当繁琐,并且不易写地正确。接下来就是展示Python奇迹的时刻了!有天我决定用Python来编写X 算法,并且我想出了一个有趣的舞蹈链变种。
算法

主要的思路是使用字典来代替双向链表来表示矩阵。我们已经有了 Y。从它那我们能快速的访问每行的列元素。现在我们还需要生成行的反向表,换句话说就是能从列中快速访问行元素。为实现这个目的,我们把X转换为字典。在上述的例子中,它应该写为
 

X = {
  1: {'A', 'B'},
  2: {'E', 'F'},
  3: {'D', 'E'},
  4: {'A', 'B', 'C'},
  5: {'C', 'D'},
  6: {'D', 'E'},
  7: {'A', 'C', 'E', 'F'}}

眼尖的读者能注意到这跟Y的表示有轻微的不同。事实上,我们需要能快速删除和添加行到每列,这就是为什么我们使用集合。另一方面,高德纳没有提到这点,实际上整个算法中所有行是保持不变的。

以下是算法的代码。
 

def solve(X, Y, solution=[]):
  if not X:
    yield list(solution)
  else:
    c = min(X, key=lambda c: len(X[c]))
    for r in list(X[c]):
      solution.append(r)
      cols = select(X, Y, r)
      for s in solve(X, Y, solution):
        yield s
      deselect(X, Y, r, cols)
      solution.pop()
 
def select(X, Y, r):
  cols = []
  for j in Y[r]:
    for i in X[j]:
      for k in Y[i]:
        if k != j:
          X[k].remove(i)
    cols.append(X.pop(j))
  return cols
 
def deselect(X, Y, r, cols):
  for j in reversed(Y[r]):
    X[j] = cols.pop()
    for i in X[j]:
      for k in Y[i]:
        if k != j:
          X[k].add(i)

真的只有 30 行!
格式化输入

在解决实际问题前,我们需要将输入转换为上面描述的格式。可以这样简单处理

X = {j: set(filter(lambda i: j in Y[i], Y)) for j in X}

但这样太慢了。假如设 X 大小为 m,Y 的大小为 n,则迭代次数为 m*n。在这例子中的数独格子大小为 N,那需要 N^5 次。我们有更好的办法。
 

X = {j: set() for j in X}
for i in Y:
  for j in Y[i]:
    X[j].add(i)

这还是 O(m*n) 的复杂度,但是是最坏情况。平均情况下它的性能会好很多,因为它不需要遍历所有的空格位。在数独的例子中,矩阵中每行恰好有 4 个条目,无论大小,因此它有N^3的复杂度。
优点

  •     简单: 不需要构造复杂的数据结构,所有用到的结构Python都有提供。
  •     可读性: 上述第一个例子是直接从Wikipedia上的范例直接转录下来的!
  •     灵活性: 可以很简单得扩展来解决数独。

求解数独

我们需要做的就是把数独描述成精确覆盖问题。这里有完整的数独解法代码,它能处理任意大小,3×3,5×5,即使是2×3,所有代码少于100行,并包含doctest!(感谢Winfried Plappert 和 David Goodger的评论和建议)

Python 相关文章推荐
Python字符转换
Sep 06 Python
Python通过RabbitMQ服务器实现交换机功能的实例教程
Jun 29 Python
详解Pytorch 使用Pytorch拟合多项式(多项式回归)
May 24 Python
Django中反向生成models.py的实例讲解
May 30 Python
对numpy.append()里的axis的用法详解
Jun 28 Python
Python基础之函数的定义与使用示例
Mar 23 Python
pytorch 归一化与反归一化实例
Dec 31 Python
详解PyQt5信号与槽的几种高级玩法
Mar 24 Python
Python txt文件常用读写操作代码实例
Aug 03 Python
Python 多进程、多线程效率对比
Nov 19 Python
python mongo 向数据中的数组类型新增数据操作
Dec 05 Python
Python打包为exe详细教程
May 18 Python
探究数组排序提升Python程序的循环的运行效率的原因
Apr 01 #Python
用Python编写分析Python程序性能的工具的教程
Apr 01 #Python
对Python新手编程过程中如何规避一些常见问题的建议
Apr 01 #Python
利用Django框架中select_related和prefetch_related函数对数据库查询优化
Apr 01 #Python
用实例详解Python中的Django框架中prefetch_related()函数对数据库查询的优化
Apr 01 #Python
Python的Django框架中的select_related函数对QuerySet 查询的优化
Apr 01 #Python
简单的Python2.7编程初学经验总结
Apr 01 #Python
You might like
CURL状态码列表(详细)
2013/06/27 PHP
PHP实现的浏览器检查类
2016/04/11 PHP
jquery JSON的解析方式
2009/07/25 Javascript
jQuery的学习步骤
2011/02/23 Javascript
js中arguments的用法(实例讲解)
2013/11/30 Javascript
JQuery使用index方法获取Jquery对象数组下标的方法
2015/05/18 Javascript
Jquery中request和request.form和request.querystring的区别
2015/11/26 Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
2015/12/14 Javascript
HTML页面,测试JS对C函数的调用简单实例
2016/08/09 Javascript
js控住DOM实现发布微博效果
2016/08/30 Javascript
微信小程序开发入门基础教程
2017/04/19 Javascript
JS实现身份证输入框的输入效果
2017/08/21 Javascript
javascript回调函数详解
2018/02/06 Javascript
vue中改变选中当前项的显示隐藏或者状态的实现方法
2018/02/08 Javascript
springMvc 前端用json的方式向后台传递对象数组方法
2018/08/07 Javascript
vue.js实现的幻灯片功能示例
2019/01/18 Javascript
atom-design(Vue.js移动端组件库)手势组件使用教程
2019/05/16 Javascript
[05:39]2014DOTA2国际邀请赛 DK晋级胜者组专访战队国士无双
2014/07/14 DOTA
Python创建系统目录的方法
2015/03/11 Python
python构建自定义回调函数详解
2017/06/20 Python
Python基于百度AI的文字识别的示例
2018/04/21 Python
python爬取足球直播吧五大联赛积分榜
2018/06/13 Python
python-itchat 获取微信群用户信息的实例
2019/02/21 Python
python+webdriver自动化环境搭建步骤详解
2019/06/03 Python
解决pyinstaller打包发布后的exe文件打开控制台闪退的问题
2019/06/21 Python
图文详解Django使用Pycharm连接MySQL数据库
2019/08/09 Python
Opencv常见图像格式Data Type及代码实例
2020/11/02 Python
python 实现ping测试延迟的两种方法
2020/12/10 Python
高中的自我鉴定
2013/12/16 职场文书
优秀团支部事迹材料
2014/02/08 职场文书
医学生求职信
2014/07/01 职场文书
2014年发展党员工作总结
2014/11/12 职场文书
导游词之寿县报恩寺
2020/01/19 职场文书
使用Python脚本对GiteePages进行一键部署的使用说明
2021/05/27 Python
Python获取指定日期是"星期几"的6种方法
2022/03/13 Python
Python matplotlib绘制雷达图
2022/04/13 Python