数独~编程作业

github:ThinMoon\031702541

PSP

PSP2.1Personal Software Process Stages预估耗时(h)实际耗时(h)
Planning计划33
Estimate估计这个任务需要多少时间2926
Development开发2621
Analysis需求分析 (包括学习新技术)46
Design Spec生成设计文档11
Design Review设计复审10.5
Coding Standard代码规范 (为目前的开发制定合适的规范)10.5
Design具体设计11
Coding具体编码1510
Code Review代码复审11
Test测试(自我测试,修改代码,提交修改)21
Reporting报告35
Test Repor测试报告11
Size Measurement计算工作量11
Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划13
合计3229

设计、实现

第一眼看到题目发现竟然是数独题,当时想这和大一大二有什么区别怎么做来做去还是算法题,在认真读完博客后发现这可不仅仅只是像以往一般只需写出算法然后控制台能够输出真确答案,往oj上一提交ac后就能两腿一伸不再管的算法题啊!
代码分两个模块分别为数独算法模块(Sudoku)、文件读取模块(FileIO)。

Sudoku模块

求解数独第一眼我看到是蒙蔽的,毕竟咱就是个算法渣渣,所以通过在网络上搜索一番后恍然大悟,原来利用回溯法便能轻松解决!

backTrace()
 /**
     * 回溯算法
     *
     * @param i 行号
     * @param j 列号
     * @param outSudokus 输出链表
     */
    public void backTrace(int i, int j, LinkedList<int[][]> outSudokus) {
        if (i == rank-1 && j == rank) {
            int[][] array = new int[9][9];
            for(int ai = 0; ai < rank; ai++) {
                for(int aj = 0; aj < rank; aj++) {
                    array[ai][aj] = matrix[ai][aj];
                }
            }
            outSudokus.add(array);
            
            return;
        }
 
        //已经到了列末尾了,还没到行尾,就换行
        if (j == rank) {
            i++;
            j = 0;
        }
 
        //如果i行j列是空格,那么才进入给空格填值的逻辑
        if (matrix[i][j] == 0) {
            for (int k = 1; k <= rank; k++) {
                //判断给i行j列放1-9中的任意一个数是否能满足规则
                if (check(i, j, k)) {
                    //将该值赋给该空格,然后进入下一个空格
                    matrix[i][j] = k;
                    backTrace(i, j + 1, outSudokus);
                    //初始化该空格
                    matrix[i][j] = 0;
                    
                }
            }
        } else {
            //如果该位置已经有值了,就进入下一个空格进行计算
            backTrace(i, j + 1, outSudokus);
        }
    }

这段代码就是利用了回溯法求解数独的核心,其原理就是通过dfs检测到每一个为零的格子后对每一个格子进行填数,填充过程为:对当前所在格从1~9依次填充,若检测函数检测到此数能够填充便进行填充,随后进入下一格知道某一格出现1~9都无法填充的情况便开始回溯将之前走过最近的路径都归为0后重新进行判断。所以,一条路走到黑然后能走遍所有路径的backTrace()支持多解的情况

chcek()
  /**
     * 判断给某行某列赋值是否符合规则
     *
     * @param row    被赋值的行号
     * @param line   被赋值的列号
     * @param number 赋的值
     * @return
     */
    public boolean check(int row, int line, int number) {
        //判断该行该列是否有重复数字
        for (int i = 0; i < rank; i++) {
            if (matrix[row][i] == number || matrix[i][line] == number) {
                return false;
            }
        }

        
        //当盘面为4,6,8,9时需要对小宫格进行判断
        int tempRow = 0;
        int tempLine = 0;
        switch(rank) {
        case 4:
            tempRow = row / 2;
            tempLine = line / 2;
            for(int i = 0; i < 2; i++) {
                for(int j = 0; j < 2; j++) {
                    if(matrix[tempRow * 2 + i][tempLine * 2 + j] == number) {
                        return false;
                    }
                }
            }
            break;
        case 6:
            tempRow = row / 2;
            tempLine = line / 3;
            for(int i = 0; i < 2; i++) {
                for(int j = 0; j < 3; j++) {
                    if(matrix[tempRow * 2 + i][tempLine * 3 + j] == number) {
                        return false;
                    }
                }
            }
            break;
        case 8:
            tempRow = row / 4;
            tempLine = line / 2;
            for(int i = 0; i < 4; i++) {
                for(int j = 0; j < 2; j++) {
                    if(matrix[tempRow * 4 + i][tempLine * 2 + j] == number) {
                        return false;
                    }
                }
            }
            break;
        case 9:
            tempRow = row / 3;
            tempLine = line / 3;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (matrix[tempRow * 3 + i][tempLine * 3 + j] == number) {
                        return false;
                    }
                }
            }
            break;
        default:
            break;
        } 
        return true;
    }

如果说backTrace()是Sudoku的灵魂,那么check()便是backTrace()的伴侣,没有check()的backTrace()就是徒有其表,只会到处瞎转。check()的作用其实也很简单,就是检测当前数字是否能够填进当前格子检测原理为判断将填入数字是否在当前行、列及小宫格中存在。
或许你会对backTrace()中的参数感到迷惑,不急我们下文详解

FileIO模块

好吧我承认算法渣渣的我输入输出流也不怎么会,不会怎么办?去学啊!于是又开始了我的学习+魔改+查api之路,终于是实现了读取txt文件及将内容输出到txt中代码,接着又传来一个噩耗“文件里有多个盘”,???黑人问号,原来我之前没看清楚一个txt文件中是有多个盘的!好吧,继续改,改改。。。最终终于有了如下代码

readFile()
    /**
     * 读入TXT文件
     * 返回一个整型数组包含里面所有数字
     */
    public int[] readFile( String pathName) {
        int[] array = new int[5000];
        int count = 0;
        
        try {
            FileReader reader = new FileReader(pathName);
            BufferedReader br = new BufferedReader(reader);
            int number = 0;
   
            while ((number = br.read()) != -1) {
                if((number-48) >= 0) {
                    array[count++] = number - 48;
                }
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return array;
    }

这个方法就是读入文件中的所有数字然后返回一个整型数组。

writeFile()
 /**
     * 写入TXT文件
     * 
     */
    public void writeFile(String pathName, LinkedList<int[][]> sudokus, int rank) {
        
        try {
            File writeName = new File(pathName); 
            writeName.createNewFile(); 
            FileWriter writer = new FileWriter(writeName);
            BufferedWriter out = new BufferedWriter(writer);
            
            for(int k = 0; k < sudokus.size(); k++) {
                int[][] array = new int[9][9];
                array = sudokus.get(k);
                for(int i = 0; i < rank; i++) {
                    for(int j = 0; j < rank; j++) {
                        out.write(array[i][j] + 48);
                        out.write(" ");
                    }
                    out.write("\n");
                }
                out.write("\n");
            }
             
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

写到这个方法或许你就懂了为什么上面backTrace()会有一个链表参数了吧,这个链表就是用来储存所有解的容器!然后在writeFile()中将所有的解打印出来!
或许你的问题又来了,readFile()返回的是一个一维数组我们该怎么操作呢? 这时候我们的splitArray()方法便应运而出啦!

splitArray()
 /**
     * 将整型readerFile读出的数组分解为不同盘面
     * 用LinkedList进行存储二位数组
     * 返回一个LinkedList
     * @param suCnt面数
     * @param rank阶数
     */
    public LinkedList<int[][]> splitArray(int[] array, int rank, int suCnt) {
        LinkedList<int[][]> sudokus = new LinkedList<int[][]>();
        int count = 0;
        for(int k = 0; k < suCnt; k++) {
            int[][] temp = new int[9][9];
            for(int i = 0; i < rank; i++) {
                for(int j = 0; j < rank; j++) {
                    temp[i][j] = array[count++];
                }
            }
            sudokus.add(temp);
        }
        
        return sudokus;
    }

测试

三阶
1644770-20190924222942212-566691875.png
四阶
1644770-20190924222952243-664505141.png
五阶
1644770-20190924222957118-367012416.png
六阶
1644770-20190924223001309-604999349.png
七阶
1644770-20190924223005833-723294311.png
八阶
1644770-20190924223010122-387974233.png
九阶
1644770-20190924223014524-1772594441.png

性能分析

性能分析主要使用jprofiler但是分析完的结果我是一脸茫然的
1644770-20190924223018746-948555834.png

1644770-20190924223023378-1336859560.png

TO THE END

这次作业其实算法不算太难但是各种新东西确实让我学到了很多,比如熟悉了git的使用,熟悉了java的基本语法,学会了去查api等等,总的来说搜获还是蛮大的!

转载于:https://www.cnblogs.com/ThinMoon/p/11581667.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数独是一种数字逻辑游戏,要求在九宫格中填写数字1到9,使得每一行、每一列和每个小九宫格内的数字都没有重复。使用Matlab来编程解决数独问题是一种非常有效和方便的方法。 首先,我们可以通过二维数组来表示数独九宫格,其中空白位置用0表示。可以使用Matlab的矩阵操作来处理这个数组,比如提取某一行或某一列的数字。 接下来,我们需要编写一个递归函数来求解数独问题。函数的输入参数是数独九宫格的二维数组,函数的输出是一个已解开的数独九宫格。 在递归函数中,我们会遍历数独九宫格,找到空白位置(值为0的位置),然后从1到9依次尝试填入数字,并检查是否满足数独规则。如果满足规则,就填入数字并调用递归函数解决下一个空白位置。如果不满足规则,就尝试下一个数字。直到所有空白位置都填满数字,即找到了一个解。 在每次递归调用中,我们需要检查填入的数字是否满足数独规则。比如,需要检查当前位置所在的行、列和小九宫格是否存在重复数字。如果存在重复数字,就终止递归调用,并回溯到上一个位置重新尝试填入其他数字。 最后,我们可以使用Matlab的图形界面来可视化数独九宫格的求解过程,或者将结果输出到文本文件中。 总的来说,通过编写一个递归函数来求解数独问题,结合Matlab的矩阵操作和图形界面功能,可以很方便地实现数独的求解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值