算法设计与分析 课程设计之N皇后问题

题目

N皇后回溯法求解空间

目的要求

目的:
1.用学到的书本知识解决实际问题的能力;
2.锻炼实际工作所需要的动手能力;
3.加强对数据结构和算法的应用;
4.锻炼自己以科学理论和工程上能力的技术,规范地开发大型、复杂、高质量的应用软件和系统软件具有关键性作用;
5.通过课程设计的实践,我们可以在程序设计方法、上机操作等基本技能和科学作风方面受到比较系统和严格的训练;
6.通过课程设计提高编写技术文档的能力;

要求:
1.针对具体问题,完成从分析问题、设计算法、分析算法、实现算法的全部过程。
2.对N皇后问题进行问题分析
3.选择合适的算法策略,对N皇后问题进行算法设计
4.使用算法对N皇后进行编码实现
5.测试用例设计、测试与运行记录
6.编写设计文档以及对课程设计总结

主要内容及技术要求

3.1 主要内容

  1. 利用《算法设计与分析》课程中所学到的编程知识和编程技巧对N皇后进行问题分析,选择合适的算法策略解决问题
  2. 对N皇后问题进行算法设计并且分析
  3. 使用算法对N皇后进行编码实现
  4. 测试用例设计、测试与运行记录
    3.2 实现功能
    使用回溯的思想对所有的可行性路径进行验证,收集所有可行性解,解决N皇后问题.

主要参考资料

1.趣学算法,作者: 陈小玉,出版社: 人民邮电出版社,出版年: 2017-7-1

2.算法导论(原书第2版),作者:[美] Thomas H.Cormen / Charles E.Leiserson / Ronald L.Rivest / Clifford Stein ,机械工业出版社,原作名: Introduction to Algorithms,译者: 潘金贵 等 ,出版年: 2006-9

3.数据结构与算法分析,作者: [美] Mark Allen Weiss ,出版社: 机械工业出版社,译者: 冯舜玺 ,出版年: 2004-1-1,原作名: Data Structures and Algorithm Analysis in C:Second Edition,

4.啊哈!算法,作者: 啊哈磊 ,出版社: 人民邮电出版社,出版年: 2014-6-1


一 概述

背景:算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

1.2 题目描述:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
算法设计与分析 课程设计之N皇后问题
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],

["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。

二 问题分析

2.1 解空间:
根据图可知,这是一个nn的棋盘范围,而总需要部署n个皇后.每个棋盘位置有两种基础可能.已经知道一行内有n种部署,总共有n行,可知初始状态下可以部署nn种解空间范围.
本题中描述的问题解有且大于1个,并且不大于n*n个
2.2 解空间结构:
分析整个题目过程,当每下一步的时候,根据其同行不能下其他皇后的要求,可知每行有1个解,根据其同列不能下其他皇后的要求,可知每列有1个解,根据斜方向不能下其他皇后的要求,可知斜行只有一个解.每走一步,下一步的皇后可走范围就会少一点,当第N个皇后在第N列占棋盘位置后即到达棋盘深度N时并且成功部署即为一个解.
2.3 剪枝:
剪纸是对一些不必要路径放弃检索,可以减少检索过程,提高效率,在N皇后问题中,当第x行(小于n行)的皇后没有位置可以占用的时候无需对x以后的皇后进行检索了,缩短了检索路径,适当剪纸提高效率.

三 算法设计

这道题需要用到回溯算法,现在在这里先简单的介绍一下这个算法:
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

3.1 回溯算法的基本思想:
从一条路往前走,能进则进,不能进则退回来,换一条路再试。用回溯算法解决问题的一般步骤为:
1、定义一个解空间,它包含问题的解。
2、利用适于搜索的方法组织解空间。
3、利用深度优先法搜索解空间。
4、利用限界函数避免移动到不可能产生解的子空间。
问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。

3.2 N皇后回溯算法思路:
3.2.1 创建一个全局数组存储每一行的Q位置:
因为题目要求不能在同一行,所以一行只能有一个Q,所以可以创建一个n大小的int数组存储每一行Q的位置,采用回溯的方法,每一行的位置都进行判断,直到排完n的位置或者第i行无法排下任何一个位置。

3.2.2 创建一个局部数组存储第i行哪些位置可以可以存放Q:
因为同一行,同一列,同一斜线的位置不能存放Q,所以创建一个局部数组,根据前i-1行内Q的位置,判断出第i行哪些位置可以存放Q,初始化为0:可以存放,值为1:不能存放,实现剪枝,缩小深搜范围。

当搜索完全部后,将所得解加入到全局list:
创建一个List<List> list存放一个解,List为一个解中的其中一行,如:…Q…
3.2.3 设置边界条件:
当搜索完n后,将搜索得出的解加入到全局list中

四 算法分析:

4.1 时间复杂度:
该算法在最好的情况下,N皇后求得第一个解需要试探n次,获取到一个解后需要nn次绘制满足条件的N皇后位置图.
方法递归n行, 每次循环 n 列, 比较 0~n-1 次。当满足边界条件的时候需要n
n次来绘制满足条件的N皇后位置图
n * n * (n - 1) +nn.
也就是 O(n^3)
4.2 空间复杂度
全局数组长度为n,方法递归n行, 每次创建长度为n的局部数组n+n
n
也就是O(n^2)

五 编码实现

import java.util.ArrayList;
import java.util.List;
public class Main {
    public static void get(List<List<String>> list,int[] num,int n,int a){
        if(a==n){
            List<String> stringList = new ArrayList<String>(n);
            StringBuffer s= new StringBuffer();
            for(int i=0;i<n;i++){
                s.setLength(0);
                for(int j=0;j<n;j++){
                    s.append(".");
                }
                s.replace(num[i],num[i]+1,"Q");
                stringList.add(s.toString());
            }
            list.add(stringList);
            return;
        }
        int[] newNum = new int[n];
        for(int i=0;i<a;i++){
            newNum[num[i]]=1;
            if((num[i]-(a-i))>=0){
                newNum[num[i]-(a-i)]=1;
            }
            if((num[i]+(a-i))<n){
                newNum[num[i]+(a-i)]=1;
            }
        }
        for(int i=0;i<newNum.length;i++){
            if(newNum[i]==0){
                num[a]=i;
                get(list,num,n,a+1);
            }
        }
    }
    public static List<List<String>> solveNQueens(int n) {
        int[] num = new int[n];
        for(int i=0;i<num.length;i++){
            num[i]=-1;
        }
        List<List<String>> list = new ArrayList<List<String>>();
        get(list,num,n,0);
        return list;
    }
    public static void main(String[] args) {
        List<List<String>> lists = solveNQueens(6);
        for(List<String> one :lists){
            for(String two :one){
                System.out.println(two);
            }
            System.out.println("");
        }
    }
}

六 测试用例设计、测试与运行记录

6.1 模拟测试数据:

如3,运行结果:
无解

如6,运行结果:
.Q…
…Q…
…Q
Q…
…Q…
…Q.

…Q…
…Q
.Q…
…Q.
Q…
…Q…

…Q…
Q…
…Q.
.Q…
…Q
…Q…

…Q.
…Q…
Q…
…Q
…Q…
.Q.…

七 总结:

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。 我们还可以通过剪枝来删去不必要的搜索.
这次算法题目的N皇后问题是一道典型的回溯法问题,通过这次算法课程设计,我对回溯法的理论知识转换为实际应用,对回溯的思想有了进一步的思考与了解.不过这题中的问题也是显而易见,因为使用的的是递归方法,当N的值过大时就会爆内存不足的问题,分析得是回溯法是一种枚举方法,当数据量过大时容易爆内存,所以需要选择适当的剪枝.在算法的设计与分析中,我也根据时间复杂度和空间复杂度对程序的性能进行了一定的评估,通过预测可以了解自己的程序是否已经最优.最后还通过测试用例验证我的算法正确性.通过这次项目,针对具体的N皇后问题应用了回溯法以及算法设计与分析,对解决问题的能力有了进一步的提高.

在此感谢帮助过我的同学和指导老师,谢谢!