递归的神奇之处在于你会发现问题竟然解决了--解N皇后谜题有感

看sicp看到8皇后谜题, 突然兴致来了,尝试独立解决(scheme代码的好处在于,即使你瞟了眼答案, 也不会有任何收获, 除了知道那儿有一坨神秘的括号和英文字符外但Python代码就不同了),成功了,而且还是N皇后算法(把N个皇后放到N*N正方形方格中有多少种方法, N为自然数).

最简单的情况是, 给你一个1*N的矩形, 需要把1个皇后放进去, 有多少种放法? 显然, 有N种放法. 这就是递归的终点. 那么如何把N*N的正方形转化为这种最简单的情况呢?

把N*N正方格分为两个矩形, 一个是1*N矩形A1, 另一个是(N-1)*N矩形B1. 则矩形B1里面必定有且仅有N-1个皇后, 现在只需要考虑如何把最后一个皇后F放到矩形A1里面就可以了.

矩形A1的皇后F的坐标(x, y), 它不能和矩形B1里面的任何一个皇后的坐标(px, px) 在同一水平(x==px), 或在同一垂直(y==py), 或在同一斜线(py-y==px-x 或 py-y==x-px), 符合条件的(x,y)加上矩形B里面的N-1个皇后的坐标组, 就构成了一个解.

上面解决了一般情形,接下来,需要把问题向最简单的情况分解:

考虑矩形B1, 它可以类似地分为一个1*N矩形和(N-2)*N矩形B2, 矩形B2里面有N-2个皇后;

考虑矩形B2, 它可以类似地分为一个1*N矩形和(N-3)*N矩形B3, 矩形B3里面有N-3个皇后;

....

考虑矩形B(N-2), 它可以类似地分为一个1*N矩形和1*N矩形B(N-1), 矩形B(N-1)里面有1个皇后;

这样, 矩形B(N-1)就是我们要的最简单的情况了.

把N*N正方形方格抽象为一个平面直角坐标系, 且0<= x, y <=N-1.

把问题输入抽象为一个整数N, 输出抽象为一组坐标组. 例如当N=4时,有2个坐标组能满足条件,分别是(0, 1), (1, 3), (2, 0), (3, 2)和(0, 2), (1, 0), (2, 3), (3, 1).

def nqueen(n):
    def recur(x):
        if x==0:
            for y in range(n):
                yield ((0, y),)
        else:
            for sln in recur(x-1):
                for y in range(n):
                    if all((y!=py and py-y!=px-x and py-y!=x-px) for px, py in sln):
                        yield sln + ((x, y),)
    return recur(n-1)

def visual(n, sln):
    for y in range(n-1,-1,-1):
        for x in range(n):
            if (x, y) in sln:
                print(‘* ‘,end=‘‘)
            else:
                print(‘0 ‘,end=‘‘)
        print()
    print()

if __name__==‘__main__‘:
    n = 4
    for sln in nqueen(n):
        print(sln)
        visual(n,sln)

结果:

>>>
((0, 1), (1, 3), (2, 0), (3, 2))
0 * 0 0
0 0 0 *
* 0 0 0
0 0 * 0 

((0, 2), (1, 0), (2, 3), (3, 1))
0 0 * 0
* 0 0 0
0 0 0 *
0 * 0 0 

问题解出的那一刻, 心里还是有些小激动, 因为这说明自己运用递归解决看似复杂问题的能力在增强. 放到以前, 我肯定在各种分类讨论中迷失, 或者想着想着就因为诸如"这太难了,头脑已想爆"的感慨而放弃.

不过, 更奇特的是, 我尝试着把nqueen的基本情况再往前推一步(纯粹是基于一种感觉):

def nqueen(n):
    def recur(x):
        if x== -1:
            yield ()
        else:
            for sln in recur(x-1):
                for y in range(n):
                    if all((y!=py and py-y!=px-x and py-y!=x-px) for px, py in sln):
                        yield sln + ((x, y),)
    return recur(n-1)

竟然完完全全没有问题!!

这一刻我对递归的神奇佩服的五体投地...

递归的神奇之处在于你会发现问题竟然解决了--解N皇后谜题有感

时间: 2024-10-06 19:11:58

递归的神奇之处在于你会发现问题竟然解决了--解N皇后谜题有感的相关文章

简述java递归与非递归算法,0-100求和,斐波那契数列,八皇后,汉诺塔问题

一:什么是递归算法? 递归算法就是直接或者间接的调用自己的方法,在达到一个条件的时候停止调用(递归出口),所以一定要找准好条件,让递归停止,否则就会是无限进行下去 二:递归程序设计的关键 1:找出调用中所需要的参数 2:返回的结果 3:递归调用结束的条件 三:递归程序注意 1:要有方法中自己调用自己 2:要有分支结构 3:要有结束的条件 四:简单叙述递归函数的优缺点 优点: 1:简洁清晰,实现容易,可读性好 2:在遍历的算法中,递归比循环更为简单 缺点: 1:效率低,使用递归函数是有空间和时间的

数据结构(DataStructure)与算法(Algorithm)、STL应用

catalogue 0. 引论 1. 数据结构的概念 2. 逻辑结构实例 2.1 堆栈 2.2 队列 2.3 树形结构 2.3.1 二叉树 3. 物理结构实例 3.1 链表 3.1.1 单向线性链表 3.1.2 单向循环链表 3.1.3 双向线性链表 3.1.4 双向循环链表 3.1.5 数组链表 3.1.6 链表数组 3.1.7 二维链表 3.2 顺序存储 4. 算法 4.1 查找算法 4.2 排序算法 0. 引论 0x1: 为什么要学习数据结构 N.沃思(Niklaus  Wirth)教授提

【web安全】第三弹:web攻防平台pentester安装及XSS部分答案解析

web for pentester是国外安全研究者开发的的一款渗透测试平台,通过该平台你可以了解到常见的Web漏洞检测技术. 下载链接及文档说明: http://pentesterlab.com/exercises/web_for_pentester/ [安装流程] 1. 虚拟机中挂载镜像. 下载好ios镜像之后,在虚拟机中创建新系统. 一路向下,创建虚拟系统. 点击启动,选择iso镜像,即可启动. 2. 设置网络. 关掉刚刚开启的系统. 点击设置,选择网络选项.如图所示设置网络 3.启动系统.

二叉树遍历,递归,栈,Morris

一篇质量非常高的关于二叉树遍历的帖子,转帖自http://noalgo.info/832.html 二叉树遍历(递归.非递归.Morris遍历) 2015年01月06日 |  分类:数据结构 |  标签:二叉树遍历 |  评论:8条评论 |  浏览:6,603次 二叉树遍历是二叉树中最基本的问题,其实现的方法非常多,有简单粗暴但容易爆栈的递归算法,还有稍微高级的使用栈模拟递归的非递归算法,另外还有不用栈而且只需要常数空间和线性时间的神奇Morris遍历算法,本文将对这些算法进行讲解和实现. 递归

Python的最大递归深度错误 “maximum recursion depth exceeded while calling a Python object”

今天在写爬虫的时候,发现了一个诡异的事情,使用str方法强制转换一个BeautifulSoup对象成字符串的时候报错了,提示是"maximum recursion depth exceeded while calling a Python object",意思大致是"当调用该对象超过最大递归深度" 报错如下:   Traceback (most recent call last):   File "<stdin>", line 1, 

二叉树之AVL树的平衡实现(递归与非递归)

这篇文章用来复习AVL的平衡操作,分别会介绍其旋转操作的递归与非递归实现,但是最终带有插入示例的版本会以递归呈现. 下面这张图绘制了需要旋转操作的8种情况.(我要给做这张图的兄弟一个赞)后面会给出这八种情况对应平衡实现. [1] 情况1-2: 这种需要旋转的结构一般称之为LL型,需要右旋 (顺时针旋转). 我用一个图来抽象一下这两个情况,画的不好,我尽量表达吧. 此时需要对A进行平衡操作,方法为: 将A的左子树换为B的右子树. B的右子树换为A. 非递归实现的代码为: 1 void rotate

python中神奇的格式化输出

python一共有两种格式化输出语法, 一种是类似于C语言printf的方式,称为 Formatting Expression >>> '%s %d-%d' % ('hello', 7, 1) 'hello 7-1' 另一种是类似于C#的方式,称为String Formatting Method Calls >>> '{0} {1}:{2}'.format('hello', '1', '7') 'hello 1:7' 第一种方式可以指定浮点数的精度,例如 >>

为什么你学不会递归?告别递归,谈谈我的一些经验

可能很多人在大一的时候,就已经接触了递归了,不过,我敢保证很多人初学者刚开始接触递归的时候,是一脸懵逼的,我当初也是,给我的感觉就是,递归太神奇了! 可能也有一大部分人知道递归,也能看的懂递归,但在实际做题过程中,却不知道怎么使用,有时候还容易被递归给搞晕.也有好几个人来问我有没有快速掌握递归的捷径啊.说实话,哪来那么多捷径啊,不过,我还是想写一篇文章,谈谈我的一些经验,或许,能够给你带来一些帮助. 为了兼顾初学者,我会从最简单的题讲起! 递归的三大要素 第一要素:明确你这个函数想要干什么 对于

递归详解

转自https://www.cnblogs.com/kubidemanong/p/10538799.html 可能很多人在大一的时候,就已经接触了递归了,不过,我敢保证很多人初学者刚开始接触递归的时候,是一脸懵逼的,我当初也是,给我的感觉就是,递归太神奇了! 可能也有一大部分人知道递归,也能看的懂递归,但在实际做题过程中,却不知道怎么使用,有时候还容易被递归给搞晕.也有好几个人来问我有没有快速掌握递归的捷径啊.说实话,哪来那么多捷径啊,不过,我还是想写一篇文章,谈谈我的一些经验,或许,能够给你带