ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第三次

2020-03-29 20:05:29  阅读:205  来源: 互联网

标签:第三次 int 代码 dfs File new 函数


软工实践第三次作业

 
软件工程https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1
作业要求 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10408
作业目标 个人编程完成数独
作业正文 见下文
其他参考文献 百度,博客园,CSDN

.Github地址

github打不开根本,挂了VPN一样打不开

1.PSP表格

Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
计划 30 20
估计这个任务需要多少时间 10 10
开发 200 150
需求分析 (包括学习新技术) 60 40
生成设计文档 60 60
设计复审 30 20
代码规范 (为目前的开发制定合适的规范) 10 10
具体设计 40 30
具体编码 120 180
代码复审 30 20
测试(自我测试,修改代码,提交修改) 400 180
报告 60 30
测试报告 60 40
计算工作量 30 15
事后总结, 并提出过程改进计划 60 40
合计 1300 845

2.解题思路

看到题目为解数独,且最大宫数为9时我便决定用深度优先搜索直接搜索出解。

3.设计过程

由于本题只是一道算法题,于是我便决定只设计一个类来求解,该类主要由main函数、输入输出函数、判断函数、深度优先搜索函数组成。

整体思路如图

dfs函数思路

3.0输入输出函数

读入参数n,m判断有几个盘面需要输入,每个盘面输入后直接调用解数独函数求解并输出

public static void inAndOut(int n,String in,String out,int m) throws IOException {
        File input = new File(in);
        File output = new File(out);
        BufferedReader reader = null;
        BufferedWriter writer = null;
        reader = new BufferedReader(new FileReader(input));
        writer = new BufferedWriter(new FileWriter(output));
        for(int l=0;l<n;l++){//l表示当前读到第几盘
            for(int i=0;i<m;i++){
                String s = reader.readLine();//读入该行
                String str = s.replace(" ","");//删除空格
                for(int j=0;j<m;j++){
                    char [] chars = str.toCharArray();//char转为int
                    array[i][j] = chars[j]-48;
                }
            }
            if(dfs(0,m)){
                for(int i=0;i<m;i++){
                    for(int j=0;j<m;j++){
                        writer.write(array[i][j]+" ");
                    }
                    writer.newLine();//写入新的一行
                }
            }else {
                writer.write("no result");
            }
            writer.newLine();
            String s = reader.readLine();
        }
        writer.close();
    }

3.1判断函数

依据要填入的空格的位置,把即将填入的数字与该空的对应的行列和宫上的数字进行比较,如果相同则不能填入该空

private static boolean isIn(int n,int x,int y,int m){
        for(int i=0;i<m;i++){
            if(array[x][i]==n||array[i][y]==n)//判断与该行该列的数字是否相同
                return false;
        }
        if(m==9) {//宫数为9时判断与该宫内的数字是否相同
            for (int i = (x / 3) * 3; i < (x / 3 + 1) * 3; i++) {
                for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if (m==4){//宫数为4时判断与该宫内的数字是否相同
            for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
                for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if(m==6) {//宫数为6时判断与该宫内的数字是否相同
            for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
                for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if(m==8) {//宫数为8时判断与该宫内的数字是否相同
            for (int i = (x / 4) * 4; i < (x / 4 + 1) * 4; i++) {
                for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        return true;
    }

3.2深度优先搜索函数

从第一行第一列的空格开始,按顺序进行搜索,如空格上以有数字,跳过该空,否则从1~9中按顺序用检验函数进行检验,如都不符合,则回到上一空,直到填满81空,结束递归。我一开始写dfs函数时定义的函数为void类型,代码如下

public static void dfs(int x,int m){
        if (x == (m*m)) {//如果以填完最后一空返回
            return;
        }
        int i = x / m;
        int j = x % m;
        if (array[i][j] == 0) {
            for (int k = 1; k <= m; k++) {
                if (isIn(n,i,j,m) {//该数字没有重复,可以填入
                    array[i][j] = k;
                    dfs(x + 1,m);//继续填下一空
                }
                array[i][j] = 0;//清除掉刚填过的数字,将盘面退回原样
            }
        } else {
            dfs(x + 1,m);//继续填下一空
        }
    }

这段代码运行完后的输出文件与输入文件完全一样,后来我上博客查看别人的代码发现有一个逻辑和我一样但是在深度优先搜索达到最后一空时便把结果输出,可以得到正确结果,与上面的代码差异如下

public static void dfs(int k,int m,String outputFilename) throws IOException {
        if (k == (m*m)) {
            try{//直接将结果输出
                File output = new File(outputFilename);
                BufferedWriter writer = null;
                writer = new BufferedWriter(new FileWriter(output));
                for(int i=0;i<m;i++){
                    for(int j=0;j<m;j++){
                        writer.write(table[i][j]+" ");
                    }
                    writer.newLine();
                }
                writer.newLine();
                writer.close();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }
        int x = k / m;
        int y = k % m;
        if (table[x][y] == 0) {
            for (int i = 1; i <= m; i++) {
                table[x][y] = i;
                if (judge(table,x, y, i,m)) {
                    dfs(k + 1,m,outputFilename);
                }
            }
            table[x][y] = 0;
        } else {
            dfs(k + 1,m,outputFilename);
        }
    }

于是我怀疑是因为没有及时输出,在到达第81空后,递归函数又一直回退到之前的状态,导致已经填好的空被消除掉,于是我增加了一个数组来存放盘面防止已填好的空被清空,代码如下

    public static int [][] res = new int[9][9];
    public static void dfs(int num,int m)
    {
        if(num==m*m){//将数组保存到另一个新数组,并返回
            for(int i=0; i<m; i++){
                for(int j=0; j<m; j++)
                    res[i][j]=array[i][j];
            }
            return ;
        }
        int i=num/m,j=num%m;
        if(array[i][j]!=0) dfs(num+1,m);
        else{
            for(int k=1; k<=m; k++)
            {
                if(isIn(k,i,j,m))
                {
                    array[i][j]=k;
                    dfs(num+1,m);
                }
            }
            array[i][j]=0;
        }
        return ;
    }

此时已经可以正确解出数独,但在我测试的时候我发现如果数独有多解且解的个数较大时,该代码需要跑很长的时间才能出结果,因为该函数会一直遍历完所有的情况才会得出结果,且需要的内存空间较大,于是我便将dfs函数改成在得出第一个解时就能直接返回的形式,代码如下

public static boolean dfs(int x,int m){
        int i,j;
        i = x/m;j = x%m;
        if(x==m*m){//如过已填完最后一空则返回true
            return true;
        }
        if(array[i][j]!=0) return dfs(x+1,m);//继续填下一空
        for(int n=1;n<=m;n++){
            if(isIn(n,i,j,m)){
                array[i][j] = n;
                if(dfs(x+1,m)) return true;//如果下一空结果正确则返回true;
                array[i][j] = 0;//如果下一空没有可以填入的数字则应清除该空以填入的数据,填入下一个可用数字
            }
        }
        return false;//该空找不到可以填入的数字,返回false以返回到上一空
    }

将函数返回值设置成boolean便可依据返回值判断是否要回到之前的状态,在第一次达到最后一空时便可以把结果输出。

4.改进思路

主要思路都以体现在上一部分的3.2深度优先搜索函数上了。

5.单元测试设计

单元测试我主要分为三个步骤进行
第一个步骤是输入输出部分的测试,代码如下

public class IOtest{
    public static int [][] array = new int[9][9];
    public static void inAndOut(int n,String in,String out,int m) throws IOException {
        File input = new File(in);
        File output = new File(out);
        BufferedReader reader = null;
        BufferedWriter writer = null;
        reader = new BufferedReader(new FileReader(input));
        writer = new BufferedWriter(new FileWriter(output));
        for(int l=0;l<n;l++){
            for(int i=0;i<m;i++){
                String s = reader.readLine();//整行读入
                String str = s.replace(" ","");//替换空格
                for(int j=0;j<m;j++){
                    char [] chars = str.toCharArray();//转化为char类型
                    array[i][j] = chars[j]-48;//转化为int类型
                }
            }
            for(int i=0;i<m;i++){
                for(int j=0;j<m;j++){
                    writer.write(array[i][j]+" ");
                }
                writer.newLine();//从新的一行开始写起
            }
            writer.newLine();//每个盘面间间隔一行
            String s = reader.readLine();
        }
        writer.close();
    }
    public static void main(String [] args) throws IOException{
        //从控制台获取参数
        char[] ch = args[1].toCharArray();
        int m = ch[0]-48;
        char[] chars = args[3].toCharArray();
        int n = chars[0] - 48;
        //调用输入输出函数
        inAndOut(n,args[5],args[7],m);
    }
}

第二个步骤是数独求解部分的测试,由于对c++比较熟悉,我一开始是用的用c++实现算法,之后才移植为java;

#include <cstdio>
using namespace std;
 
    int array[9][9];
    bool isIn(int n,int x,int y,int m){
        for(int i=0;i<m;i++){
            if(array[x][i]==n||array[i][y]==n)
                return false;
        }
        if(m==9) {
            for (int i = (x / 3) * 3; i < (x / 3 + 1) * 3; i++) {
                for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if (m==4){
            for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
                for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if(m==6) {
            for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
                for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        if(m==8) {
            for (int i = (x / 4) * 4; i < (x / 4 + 1) * 4; i++) {
                for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
                    if (array[i][j] == n)
                        return false;
                }
            }
        }
        return true;
    }

    bool dfs(int x,int m){
        int i,j;
        i = x/m;j = x%m;
        if(x==m*m){
            return true;
        }
        if(array[i][j]!=0) return dfs(x+1,m);
        for(int n=1;n<=m;n++){
            if(isIn(n,i,j,m)){
                array[i][j] = n;
                if(dfs(x+1,m)) return true;
                array[i][j] = 0;
            }
        }
        return false;
    }
    int main(){
		int m = 6;//指定宫数
    	for(int i=0; i<m; i++)
        	for(int j=0; j<m; j++)
            	scanf("%d",&array[i][j]);
    	dfs(0,m);
    	for(int i=0; i<m; i++){
        	for(int j=0; j<m; j++)
            	printf("%d ",array[i][j]);
        	printf("\n");
    	}
    	return 0;
    }

第三个步骤便是将之前两部分整合起来,对完整的代码进行测试。

6.Code Quality Analysis工具的分析结果

采用jprofiler分析,结果如下


占用cpu运行时间最长的为我在输入时使用过的String.replace函数,接下来就是dfs函数和判断函数,运行次数最多的为判断函数,判断函数主要是被dfs函数所调用,如果想进行优化的话最好还是对dfs函数进行优化。
下面试代码规范分析的截图,主要问题在于一些变量名和定义变量时不够规范

7.心路历程与收获

本次作业还是比较简单的,对我来说主要难点就是深度优先搜索函数的设计,本次作业加深了我对深度优先搜索和递归的理解,但对于如何构建软件并没有很大的帮助。

 

自评表

学号姓名作业头GitHub项目地址代码要求经过凑得工具的分析消除警告PSP表格解题思路描述代码如何组织关键函数流程图单元测试的设计找出性能瓶颈改进显示关键代码解释思路与注释说明心路历程和感想总分
    2分 1分 2分 1分 1分 0.5分 1分 0.5分 0.5分 0.5分 0.5分 0.5分 1分 12分
20177599 胡宇 2 0 1 1 1 0.2 0.5 0.2 0.2 0.2 0.5 0.2 1 8

标签:第三次,int,代码,dfs,File,new,函数
来源: https://www.cnblogs.com/a2228618156/p/12594366.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有