仅利用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连接oracle数据库实例
Oct 17 Python
在Python中使用swapCase()方法转换大小写的教程
May 20 Python
Python中操作文件之write()方法的使用教程
May 25 Python
Python实现建立SSH连接的方法
Jun 03 Python
python如何查看系统网络流量的信息
Sep 12 Python
python简易远程控制单线程版
Jun 20 Python
python创建文件备份的脚本
Sep 11 Python
Python Numpy:找到list中的np.nan值方法
Oct 30 Python
python批量修改xml属性的实现方式
Mar 05 Python
Python如何实现定时器功能
May 28 Python
Django DRF路由与扩展功能的实现
Jun 03 Python
Python经常使用的一些内置函数
Apr 11 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
php echo()和print()、require()和include()函数区别说明
2010/03/27 PHP
php用户注册时常用的检验函数实例总结
2014/12/22 PHP
编写PHP脚本使WordPress的主题支持Widget侧边栏
2015/12/14 PHP
smarty高级特性之过滤器的使用方法
2015/12/25 PHP
PHP性能优化大全(php.ini)
2016/05/20 PHP
ThinkPHP 整合Bootstrap Ajax分页样式
2016/12/23 PHP
php基于自定义函数记录log日志方法
2017/07/21 PHP
laravel-admin的图片删除实例
2019/09/30 PHP
判断JavaScript对象是否可用的最正确方法分析
2008/10/03 Javascript
js的闭包的一个示例说明
2008/11/18 Javascript
JS 实现导航栏悬停效果(续)
2013/09/24 Javascript
原生js实现日期联动
2015/01/12 Javascript
javascript的BOM汇总
2015/07/16 Javascript
javascript性能优化之事件委托实例详解
2015/12/12 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
JS正则表达式判断有效数实例代码
2017/03/13 Javascript
Node.js+jade抓取博客所有文章生成静态html文件的实例
2017/09/19 Javascript
详解angularjs实现echart图表效果最简洁教程
2017/11/29 Javascript
JS实现动态添加外部js、css到head标签的方法
2019/06/05 Javascript
bootstrap datepicker的基本使用教程
2019/07/09 Javascript
Python使用稀疏矩阵节省内存实例
2014/06/27 Python
对于Python的Django框架使用的一些实用建议
2015/04/03 Python
Python操作列表之List.insert()方法的使用
2015/05/20 Python
TensorFlow实现卷积神经网络CNN
2018/03/09 Python
用Python将mysql数据导出成json的方法
2018/08/21 Python
用sqlalchemy构建Django连接池的实例
2019/08/29 Python
Python3 main函数使用sys.argv传入多个参数的实现
2019/12/25 Python
Pytorch高阶OP操作where,gather原理
2020/04/30 Python
Python列表去重复项的N种方法(实例代码)
2020/05/12 Python
机械工程师求职自我评价
2013/09/23 职场文书
成人毕业生自我鉴定
2013/10/18 职场文书
小学生成绩单评语
2014/12/31 职场文书
稽核岗位职责
2015/02/10 职场文书
教师信息技术学习心得体会
2016/01/21 职场文书
详解MySQL 联合查询优化机制
2021/05/10 MySQL
Python编程中Python与GIL互斥锁关系作用分析
2021/09/15 Python