仅利用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 18 Python
详解Python中映射类型(字典)操作符的概念和使用
Aug 19 Python
python构建自定义回调函数详解
Jun 20 Python
Python解析json之ValueError: Expecting property name enclosed in double quotes: line 1 column 2(char 1)
Jul 06 Python
python计算auc指标实例
Jul 13 Python
Python寻找两个有序数组的中位数实例详解
Dec 05 Python
Python3.5 处理文本txt,删除不需要的行方法
Dec 10 Python
Python中栈、队列与优先级队列的实现方法
Jun 30 Python
django 使用全局搜索功能的实例详解
Jul 18 Python
Python文本文件的合并操作方法代码实例
Mar 31 Python
浅谈tensorflow模型保存为pb的各种姿势
May 25 Python
浅谈Python 命令行参数argparse写入图片路径操作
Jul 12 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中使用接口实现工厂设计模式的代码
2012/06/17 PHP
简单实用的网站PHP缓存类实例
2014/07/18 PHP
php采集自中央气象台范围覆盖全国的天气预报代码实例
2015/01/04 PHP
php生成圆角图片的方法
2015/04/07 PHP
php微信公众号开发(4)php实现自定义关键字回复
2016/12/15 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
jQuery下通过replace字符串替换实现大小图片切换
2012/05/22 Javascript
原生js制作简单的数字键盘
2015/04/24 Javascript
值得分享的Bootstrap Table使用教程
2016/11/23 Javascript
Javascript基础回顾之(三) js面向对象
2017/01/31 Javascript
Easyui Tree获取当前选择节点的所有顶级父节点
2017/02/14 Javascript
Vue中用props给data赋初始值遇到的问题解决
2018/11/27 Javascript
Vue CLI3创建项目部署到Tomcat 使用ngrok映射到外网
2019/05/16 Javascript
react 生命周期实例分析
2020/05/18 Javascript
pycharm 使用心得(五)断点调试
2014/06/06 Python
Python中分数的相关使用教程
2015/03/30 Python
解决Python中由于logging模块误用导致的内存泄露
2015/04/23 Python
Python搭建HTTP服务器和FTP服务器
2017/03/09 Python
Python调用微信公众平台接口操作示例
2017/07/08 Python
详解Python 实现元胞自动机中的生命游戏(Game of life)
2018/01/27 Python
在django中,关于session的通用设置方法
2019/08/06 Python
Python通过Manager方式实现多个无关联进程共享数据的实现
2019/11/07 Python
OpenCV Python实现图像指定区域裁剪
2021/03/12 Python
如何以Winsows Service方式运行JupyterLab
2020/08/30 Python
python爬虫爬取网页数据并解析数据
2020/09/18 Python
使用CSS实现阅读进度条
2017/02/27 HTML / CSS
解析html5 canvas实现背景鼠标连线动态效果代码
2019/06/17 HTML / CSS
Old Navy加拿大官网:美式休闲服饰品牌
2017/09/26 全球购物
西班牙高科技产品购物网站:MejorDeseo
2019/09/08 全球购物
如果一个类实现了多个接口但是这些接口有相同的方法名将会怎样
2013/06/16 面试题
中学运动会广播稿
2014/01/19 职场文书
2014年社区庆元旦活动方案
2014/03/08 职场文书
自考毕业自我鉴定
2014/03/18 职场文书
超市仓管员岗位职责范本
2014/09/18 职场文书
Python爬虫进阶之Beautiful Soup库详解
2021/04/29 Python
python 开心网和豆瓣日记爬取的小爬虫
2021/05/29 Python