八皇后问题是十九世纪著名的数学家高斯1850年提出 。以下为python语句的八皇后代码,摘自《Python基础教程》,代码相对于其他语言,来得短小且一次性可以打印出92种结果。同时可以扩展为九皇后,十皇后问题。

  问题:在一个8*8棋盘上,每一行放置一个皇后旗子,且它们不冲突。冲突定义:同一列不能有两个皇后,每一个对角线也不能有两个皇后。当然,三个皇后也是不行的,四个也是不行的,凭你的智商应该可以理解吧。

    解决方案:回溯与递归

  介绍:

1.回溯法

回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。参见百度百科:http://baike.baidu.com/view/45.htm?fr=aladdin

2.递归法

阶乘 n! = 1 x 2 x 3 x ... x n

用函数fact(n)表示,可以看出:

fact(1) = 1

fact(n) = n!

= 1 x 2 x 3 x ... x (n-1) x n

= (n-1)! x n

= fact(n-1) x n

于是,fact(n)用递归的方式写出来就是:

 def fact(n):
     if n==1:
         return 1
     return n * fact(n - 1)

如果计算fact(5),结果如下:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

  

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000)

>>> fact(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in fact
  ...
  File "<stdin>", line 4, in fact
RuntimeError: maximum recursion depth exceeded

  

解决递归调用栈溢出的方法是通过尾递归优化

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

如:

 def factorial(n, acc=1):
   "calculate a factorial"
   if n == 0:
     return acc
   return factorial(n-1, n*acc)

函数返回时只调用了它本身 factorial(n-1, n*acc)

问题是Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题,有一个针对尾递归优化的decorator,可以参考源码:

http://code.activestate.com/recipes/474088-tail-call-optimization-decorator/

参考http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00137473836826348026db722d9435483fa38c137b7e685000

python源码:

# -*- coding: utf-8 -*-
#python默认为ascii编码,中文编码可以用utf-8
import random
#随机模块

def conflict(state,col):
    #冲突函数,row为行,col为列
    row=len(state)
    for i in range(row):
        if abs(state[i]-col) in (0,row-i):#重要语句
            return True
    return False

def queens(num=8,state=()):
    #生成器函数
    for pos in range(num):
        if not conflict(state, pos):
            if len(state)==num-1:
                yield(pos,)
            else:
                for result in queens(num, state+(pos,)):
                    yield (pos,)+result

def queenprint(solution):
    #打印函数
    def line(pos,length=len(solution)):
        return '. '*(pos)+'X '+'. '*(length-pos-1)
    for pos in solution:
        print line(pos)

for solution in list(queens(8)):
    print solution

print '  total number is '+str(len(list(queens())))
print '  one of the range is:\n'
queenprint(random.choice(list(queens())))

结果:

(0, 4, 7, 5, 2, 6, 1, 3)
(0, 5, 7, 2, 6, 3, 1, 4)
(0, 6, 3, 5, 7, 1, 4, 2)
(0, 6, 4, 7, 1, 3, 5, 2)
(1, 3, 5, 7, 2, 0, 6, 4)
(1, 4, 6, 0, 2, 7, 5, 3)
(1, 4, 6, 3, 0, 7, 5, 2)
(1, 5, 0, 6, 3, 7, 2, 4)
(1, 5, 7, 2, 0, 3, 6, 4)
(1, 6, 2, 5, 7, 4, 0, 3)
(1, 6, 4, 7, 0, 3, 5, 2)
(1, 7, 5, 0, 2, 4, 6, 3)
(2, 0, 6, 4, 7, 1, 3, 5)
(2, 4, 1, 7, 0, 6, 3, 5)
(2, 4, 1, 7, 5, 3, 6, 0)
(2, 4, 6, 0, 3, 1, 7, 5)
(2, 4, 7, 3, 0, 6, 1, 5)
(2, 5, 1, 4, 7, 0, 6, 3)
(2, 5, 1, 6, 0, 3, 7, 4)
(2, 5, 1, 6, 4, 0, 7, 3)
(2, 5, 3, 0, 7, 4, 6, 1)
(2, 5, 3, 1, 7, 4, 6, 0)
(2, 5, 7, 0, 3, 6, 4, 1)
(2, 5, 7, 0, 4, 6, 1, 3)
(2, 5, 7, 1, 3, 0, 6, 4)
(2, 6, 1, 7, 4, 0, 3, 5)
(2, 6, 1, 7, 5, 3, 0, 4)
(2, 7, 3, 6, 0, 5, 1, 4)
(3, 0, 4, 7, 1, 6, 2, 5)
(3, 0, 4, 7, 5, 2, 6, 1)
(3, 1, 4, 7, 5, 0, 2, 6)
(3, 1, 6, 2, 5, 7, 0, 4)
(3, 1, 6, 2, 5, 7, 4, 0)
(3, 1, 6, 4, 0, 7, 5, 2)
(3, 1, 7, 4, 6, 0, 2, 5)
(3, 1, 7, 5, 0, 2, 4, 6)
(3, 5, 0, 4, 1, 7, 2, 6)
(3, 5, 7, 1, 6, 0, 2, 4)
(3, 5, 7, 2, 0, 6, 4, 1)
(3, 6, 0, 7, 4, 1, 5, 2)
(3, 6, 2, 7, 1, 4, 0, 5)
(3, 6, 4, 1, 5, 0, 2, 7)
(3, 6, 4, 2, 0, 5, 7, 1)
(3, 7, 0, 2, 5, 1, 6, 4)
(3, 7, 0, 4, 6, 1, 5, 2)
(3, 7, 4, 2, 0, 6, 1, 5)
(4, 0, 3, 5, 7, 1, 6, 2)
(4, 0, 7, 3, 1, 6, 2, 5)
(4, 0, 7, 5, 2, 6, 1, 3)
(4, 1, 3, 5, 7, 2, 0, 6)
(4, 1, 3, 6, 2, 7, 5, 0)
(4, 1, 5, 0, 6, 3, 7, 2)
(4, 1, 7, 0, 3, 6, 2, 5)
(4, 2, 0, 5, 7, 1, 3, 6)
(4, 2, 0, 6, 1, 7, 5, 3)
(4, 2, 7, 3, 6, 0, 5, 1)
(4, 6, 0, 2, 7, 5, 3, 1)
(4, 6, 0, 3, 1, 7, 5, 2)
(4, 6, 1, 3, 7, 0, 2, 5)
(4, 6, 1, 5, 2, 0, 3, 7)
(4, 6, 1, 5, 2, 0, 7, 3)
(4, 6, 3, 0, 2, 7, 5, 1)
(4, 7, 3, 0, 2, 5, 1, 6)
(4, 7, 3, 0, 6, 1, 5, 2)
(5, 0, 4, 1, 7, 2, 6, 3)
(5, 1, 6, 0, 2, 4, 7, 3)
(5, 1, 6, 0, 3, 7, 4, 2)
(5, 2, 0, 6, 4, 7, 1, 3)
(5, 2, 0, 7, 3, 1, 6, 4)
(5, 2, 0, 7, 4, 1, 3, 6)
(5, 2, 4, 6, 0, 3, 1, 7)
(5, 2, 4, 7, 0, 3, 1, 6)
(5, 2, 6, 1, 3, 7, 0, 4)
(5, 2, 6, 1, 7, 4, 0, 3)
(5, 2, 6, 3, 0, 7, 1, 4)
(5, 3, 0, 4, 7, 1, 6, 2)
(5, 3, 1, 7, 4, 6, 0, 2)
(5, 3, 6, 0, 2, 4, 1, 7)
(5, 3, 6, 0, 7, 1, 4, 2)
(5, 7, 1, 3, 0, 6, 4, 2)
(6, 0, 2, 7, 5, 3, 1, 4)
(6, 1, 3, 0, 7, 4, 2, 5)
(6, 1, 5, 2, 0, 3, 7, 4)
(6, 2, 0, 5, 7, 4, 1, 3)
(6, 2, 7, 1, 4, 0, 5, 3)
(6, 3, 1, 4, 7, 0, 2, 5)
(6, 3, 1, 7, 5, 0, 2, 4)
(6, 4, 2, 0, 5, 7, 1, 3)
(7, 1, 3, 0, 6, 4, 2, 5)
(7, 1, 4, 2, 0, 6, 3, 5)
(7, 2, 0, 5, 1, 4, 6, 3)
(7, 3, 0, 2, 5, 1, 6, 4)
  total number is 92
  one of the range is:

X . . . . . . .
. . . . . . X .
. . . X . . . .
. . . . . X . .
. . . . . . . X
. X . . . . . .
. . . . X . . .
. . X . . . . .

  

源码解析:

主要利用冲突函数检测冲突,如果冲突则回溯,递归用到python的yield语句,该语句涉及python的生成器。

冲突函数:

 def conflict(state,col):
     #冲突函数,row为行,col为列
     row=len(state)
     for i in range(row):
         if abs(state[i]-col) in (0,row-i):#重要语句
             return True
     return False

state为皇后的状态,类型是一个元组,如(7, 3, 0, 2, 5, 1, 6, 4),元组是不可变对象,一经创建不能修改,元组是创建生成器的一种方法。

步骤:

假设第一行到第三行的皇后都没冲突,这个时候要检测第四行皇后是否冲突。如第一行皇后在第五列,第二行皇后在第八列,第三行皇后在第四列,检验第四行皇后放在哪一列不会冲突。

. . . . X . . . 
. . . . . . . X 
. . . X . . . .

这时state=(4,7,3),col=?

1.得出目前没冲突行数row

   row=len(state)

2.从1~row行依次检测是否与row+1行皇后冲突

 for i in range(row):

3.如果row+1行皇后所在的列col与其他行皇后的列相同或处于对角线,则冲突

if abs(state[i]-col) in (0,row-i):#重要语句
            return True

以上语句翻译为(其他行所在的列-要求检测所在行的列)相差范围为0或row-i则冲突。

傻瓜式教学:

第一行与第四行冲突,要么在同一列,要么在对角线,当对角线时列数相差3,因为第一行与第二行对角线相差1,第二行与第三行对角线相差1,则第一行与第三行对角线相差2,以此类推,第一行与第四行冲突,则相差3.

当第四行所在列col=4,这时abs ( state[0]-4 ) in (0 , 3-0) 为真,因为4-4=0,如“:

. . . . X . . . 
. . . . . . . X 
. . . X . . . .

. . . . X . . .  同列冲突

当第四行所在列col=7,这时abs ( state[0]-7 ) in (0 , 3-0) 为真,因为abs (4-7)=3,如:

. . . . X . . . 
. . . . . . . X 
. . . X . . . .

. . . . . . . . X 对角线冲突

你们这么聪明,该重要语句应该懂吧。

生成器函数:

 def queens(num=8,state=()):
     #生成器函数
     for pos in range(num):
         if not conflict(state, pos):
             if len(state)==num-1:
                 yield(pos,)
             else:
                 for result in queens(num, state+(pos,)):
                     yield (pos,)+result

生成器:

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

参考:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00138681965108490cb4c13182e472f8d87830f13be6e88000

步骤:

1.下面该语句为构建所有皇后摆放情况打下基础。可以尝试所有情况。

for pos in range(num):

2.如果不冲突,则递归构造棋盘。

if not conflict(state, pos):

3.如果棋盘状态state已经等于num-1,即到达倒数第二行,而这时最后一行皇后又没冲突,直接yield,打出其位置(pos, ),Python在显示只有1个元素的元组时,也会加一个逗号,,以免你误解成数学计算意义上的括号。

否则递归,打印(pos , )+ result

            if len(state)==num-1:
                yield(pos,)
            else:
                for result in queens(num, state+(pos,)):
                    yield (pos,)+result

傻瓜式教学:
例如pos=0,第一行放在第一列,这时不会冲突,但是不会进入if,因为还没到达倒数第二行,进入else后,再调用queens(num, state+(pos,),这时进入第二行,再次递归展开则是queens(num,state+(pos, )+(pos, ) ),到达最后一行时返回(pos, ),再返回倒数第二行,再返回倒数第三行,最后到达最开始那层(pos, )+result, pos 为第一行皇后所在列,result包含第二行皇后所在列和另一个result,就是这么复杂,希望好好琢磨。

优美格式的打印函数就不讲了。

讲讲打印所有结果

for solution in queens(8):
    print solution

queens(8)因为生成器函数的for循环,每一次循环都会yield一个元组出来,所以有很多种情况,可以把它全部打出来。

也可以用list包装成列表再统计一下多少种数目。

print '  total number is '+str(len(list(queens()))

随机优美打印一个棋盘情况:

print '  one of the range is:\n'
queenprint(random.choice(list(queens())))

八皇后,回溯与递归(Python实现)的更多相关文章

  1. 算法学习 八皇后问题的递归实现 java版 回溯思想

    1.问题描述 八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或 ...

  2. js实现八皇后,回溯法

    八皇后问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法? 两个皇后不能同时在同一行,同一列,和斜对角线的位置上,使用回溯法解决. 从第一行选个位置开始放棋 ...

  3. C#中八皇后问题的递归解法——N皇后

    百度测试部2015年10月份的面试题之——八皇后. 八皇后问题的介绍在此.以下是用递归思想实现八皇后-N皇后. 代码如下: using System;using System.Collections. ...

  4. [poj百练]2754:八皇后 回溯

    描述 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题. 对于某个满足要求的8皇后 ...

  5. 八皇后问题 dfs/递归

    #include <bits/stdc++.h> using namespace std; const int maxn = 55; int ans=0; int vis_Q[maxn]; ...

  6. C语言数据结构----递归的应用(八皇后问题的具体流程)

    本节主要讲八皇后问题的基本规则和递归回溯算法的实现以及具体的代码实现和代码分析. 转载请注明出处.http://write.blog.csdn.net/postedit/10813257 一.八皇后问 ...

  7. 八皇后非递归(仅使用一个数组且可扩展为N皇后问题)

    </pre><pre name="code" class="cpp">/* Theme:八皇后(非递归) Coder:秒针的声音 Tim ...

  8. LeetCode 31:递归、回溯、八皇后、全排列一篇文章全讲清楚

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天我们讲的是LeetCode的31题,这是一道非常经典的问题,经常会在面试当中遇到.在今天的文章当中除了关于题目的分析和解答之外,我们还会 ...

  9. LeetCode 回溯法 别人的小结 八皇后 递归

    #include <iostream> #include <algorithm> #include <iterator> #include <vector&g ...

随机推荐

  1. Javascript定时跳转

    因为做项目,用到跳转回上级页面,这里设置定时3秒跳转到目标页面 <script> setInterval("myInterval()",3000);//1000为1秒钟 ...

  2. B/S和C/S的区别(转)

    一.什么是CS和BS结构? 1.C/S又称Client/Server或客户/服务器模式.服务器通常采用高性能的PC.工作站或小型机,并采用大型数据库系统,如Oracle.Sybase.Informix ...

  3. Eclipse下Android开发的问题:Failed to install AndroidPhone.apk on device 'emulator-5554': timeout 解决办法

    在window->preferences->Android->DDMS->ADB connection time out (ms): 将这个值设置的大一些,默认为5000,我设 ...

  4. java微信开发(wechat4j)——得到微信请求参数

    微信平台会在请求的post数据中带有一些参数,例如用户的openid之类的信息,当你使用了wechat4j之后,得到这些信息是非常方便的. public class Lejian extends We ...

  5. SharePoint 2013 新功能探索 之 列表等级设置

    一.列表等级及赞功能 ,在SharePoint 2010 中,对列表的等级设定,需要一定时间才能看到,现在可以实时同步,评分人数也能显示出来 等级分为两类 赞和星级评定   

  6. Sublime Text3 快捷键汇总

    Ctrl+D 选词 (反复按快捷键,即可继续向下同时选中下一个相同的文本进行同时编辑)Ctrl+G 跳转到相应的行Ctrl+J 合并行(已选择需要合并的多行时)Ctrl+L 选择整行(按住-继续选择下 ...

  7. 利用ng-click、ng-switch和click-class制作切换的tabl

    效果如下图,当分别点击1,2,3时,下面的不同颜色的div会切换 <html ng-app> <head> <title></title> <sc ...

  8. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q88-Q90)

    Question 88You have a Microsoft .NET Framework console application that uses the SharePoint client o ...

  9. Android 首页图片轮播

    1.网络上的的一个框架,已经在github 上开源 github  : https://github.com/gcgongchao/flashview 相关博客 : http://www.eoeand ...

  10. Mac平台下启动MySQL到完全终止MySQL----终端八步走

    1.选中Finder的情况下,快捷键进入搜索目录:/usr/local 然后进入mysql目录下: 2.右键 "从这里启动" 打开终端: 3.输入执行:./scripts/mysq ...