软件工程第三次做业

这个做业属于哪一个课程 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1
这个做业要求在哪里 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494
这个做业的目标 我的编写程序——实现数独算法
做业正文 http://www.javashuo.com/article/p-vhjfhmqc-mm.html
其余参考文献 百度

https://github.com/waterrr/Software-Engineering-Homework/blob/master/sudoku.pyhtml

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 30
Estimate 估计这个任务须要多少时间 20 30
Development 开发 180 150
Analysis 需求分析 (包括学习新技术) 30 120
Design Spec 生成设计文档 30 30
Design Review 设计复审 15 15
Coding Standard 代码规范 (为目前的开发制定合适的规范) 15 15
Design 具体设计 30 60
Coding 具体编码 30 30
Code Review 代码复审 30 60
Test 测试(自我测试,修改代码,提交修改) 30 120
Reporting 报告 30 30
Test Repor 测试报告 30 20
Size Measurement 计算工做量 20 10
Postmortem & Process Improvement Plan 过后总结, 并提出过程改进计划 30 30
合计 530 750

写在前面

其实一开始听到这个消息我实际上是拒绝的,我根本不会这么复杂的算法啊,上次走迷宫我都走了好久,彻底不会,我对python的认知,仅仅存在会写简单脚本的水平,一旦涉及到复杂的算法,对不起,打扰了。python

可是问题来了,既然已经布置了,仍是硬着头皮也要作完,没办法,无奈之下,在网上找了大量资料,包括但不限于朱旭炜大佬、刘涛大佬、刘印传大佬、邓畅伟大佬、唐巍神仙的博客、github等,在这里表示感谢,虽然参考了这么多大佬的写法,感受真的是一个比一个神仙,算法写得我根本看不懂,无法理解具体过程。git

可是好在,我仍是写出来了,在网上参考了一个比较容易理解的方法,鬼知道我经历了什么。github

需求分析以及代码实现

首先,我跑去看了数独的规则,详细研究了一下,虽然这是一个很经典的益智游戏,可是我真的从小到大都没玩过,可能我对这些须要高智商的游戏不怎么感兴趣,题目要求咱们作一个解数独的程序。固然三宫格的就没什么可玩的呢,就暂时不考虑,咱们直接从四宫格开始吧。算法

一、创建坐标

咱们首先把棋盘化为一个坐标,创建一个坐标系,把每一个位置都变为一个坐标。编程

class point:
"""
初始化方法
创建坐标系
"""
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.available = []
        self.value = 0

二、判断行列

判断行重复和列重复,简单判断一下就行了,好比在第一行的数字为[4,0,0,0],那么第一行填充数只能为[1,2,3]。app

def rowNum(p,sudoku):
"""
判断行
:return:
"""
    row = set(sudoku[p.y * 4:(p.y + 1) * 4])
    row.remove(0)
    return row


def colNum(p,sudoku):
"""
判断列
:return:
"""
    col = []
    length = len(sudoku)
    for i in range(p.x,length,4):
        col.append(sudoku[i])
    col = set(col)
    col.remove(0)
    return col

三、找到须要填充的位置

如图所示。假设给定一个四宫格,须要填充的地方就是数字0所在的地方,因此咱们如今把他们都找出来。函数

举个例子,第一行为[4,0,0,0],那么在坐标关系上,4表明(0,0),那么须要填充的位置就为(1,0)、(2,0)、(3,0)。性能

def initPoint(sudoku):
"""
找到须要填充的位置
:return:
"""
    pointList = []
    length = len(sudoku)
    for i in range(length):
        if sudoku[i] == 0:
            p = point(i % 4,i // 4)   #遍历全部的点
            for j in range(1,5):      #找到不重复的结果
                if j not in rowNum(p,sudoku) and j not in colNum(p,sudoku):
                    p.available.append(j)
            pointList.append(p)
    return pointList

四、填充位置

对每一个位置进行全部可能性地填充重复操做,直到知足数独的要求为止。单元测试

def tryInsert(p,sudoku):
"""
对于每一个须要的位置进行填充
:return:
"""
    availNum = p.available
    for v in availNum:
        p.value = v
        if check(p,sudoku):
            sudoku[p.y*4 + p.x] = p.value
            if len(pointList) <= 0:
                exit()
            p2 = pointList.pop()
            tryInsert(p2,sudoku)
            sudoku[p2.y * 4 + p2.x] = 0
            sudoku[p.y * 4 + p.x] = 0
            p2.value = 0
            pointList.append(p2)
        else:
            pass

五、判断填充条件

在填充以前,首先检查须要填充的点是否为0,而后再次检查点有没有重复。

def check(p,sudoku):
"""
判断填充条件
:return:
""" 
    #若是为0就不填充
    if p.value == 0:
        return False
    #再次检查填充的点是否与当前已经填充的点重复
    if p.value not in rowNum(p,sudoku) and p.value not in colNum(p,sudoku):
        return True
    else:
        return False

六、主函数

if __name__ == '__main__':
    sudoku = [
        4,0,0,0,
        0,0,0,1,
        0,0,1,0,
        0,0,3,4,
    ]
    pointList = initPoint(sudoku)
    p = pointList.pop()
    tryInsert(p,sudoku)

半成品代码

至此,咱们就大概得出了一个基本的模型,咱们来看一下如今的半成品代码(并非最终版)。

class point:
"""
初始化方法
创建坐标系
"""
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.available = []
        self.value = 0


def rowNum(p,sudoku):
"""
判断行
:return:
"""
    row = set(sudoku[p.y * 4:(p.y + 1) * 4])
    row.remove(0)
    return row


def colNum(p,sudoku):
"""
判断列
:return:
"""
    col = []
    length = len(sudoku)
    for i in range(p.x,length,4):
        col.append(sudoku[i])
    col = set(col)
    col.remove(0)
    return col


def initPoint(sudoku):
"""
找到须要填充的位置
:return:
"""
    pointList = []
    length = len(sudoku)
    for i in range(length):
        if sudoku[i] == 0:
            p = point(i % 4,i // 4)   #遍历全部的点
            for j in range(1,5):      #找到不重复的结果
                if j not in rowNum(p,sudoku) and j not in colNum(p,sudoku):
                    p.available.append(j)
            pointList.append(p)
    return pointList


def tryInsert(p,sudoku):
"""
对于每一个须要的位置进行填充
:return:
"""
    availNum = p.available
    for v in availNum:
        p.value = v
        if check(p,sudoku):
            sudoku[p.y*4 + p.x] = p.value
            if len(pointList) <= 0:
                exit()
            p2 = pointList.pop()
            tryInsert(p2,sudoku)
            sudoku[p2.y * 4 + p2.x] = 0
            sudoku[p.y * 4 + p.x] = 0
            p2.value = 0
            pointList.append(p2)
        else:
            pass


def check(p,sudoku):
"""
判断填充条件
:return:
""" 
    #若是为0就不填充
    if p.value == 0:
        return False
    #再次检查填充的点是否与当前已经填充的点重复
    if p.value not in rowNum(p,sudoku) and p.value not in colNum(p,sudoku):
        return True
    else:
        return False
        

if __name__ == '__main__':
    sudoku = [
        4,0,0,0,
        0,0,0,1,
        0,0,1,0,
        0,0,3,4,
    ]
    pointList = initPoint(sudoku)
    p = pointList.pop()
    tryInsert(p,sudoku)

固然,我忘记把DEBUG阶段写出来了,固然没人知道个人DEBUG过程的痛苦过程,改一个BUG,多了十几个报错,可是好在我花了很长一段时间把报错都解决了。都是一些常见的错误,缩进问题,中文符号问题,变量名打错这几个问题,还好逻辑上没有错误,否则会花上更多的时间。

至此,一段解数独的胶水代码就完成了,咱们来看看结果吧!

结果正确!!!!!

此时个人心情:

等等,好像离要求的还差很远,可是这不是正常现象吗。

静态代码检查

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   sudoku.py
@Contact :   admin@gksec.com
@License :   (C)Copyright 2007, GNU General Public License V3

@Modify Time      @Author    @Version    @Desciption
------------      -------    --------    -----------
2020/3/26 15:28   W4ter      1.0         None
"""

class point:
    """
初始化方法
创建坐标系
    """
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.available = []
        self.value = 0


def rowNum(p,sudoku):
    """
判断行
:return:
    """
    row = set(sudoku[p.y * 4:(p.y + 1) * 4])
    row.remove(0)
    return row


def colNum(p,sudoku):
    """
判断列
:return:
    """
    col = []
    length = len(sudoku)
    for i in range(p.x,length,4):
        col.append(sudoku[i])
    col = set(col)
    col.remove(0)
    return col


def initPoint(sudoku):
    """
找到须要填充的位置
:return:
    """
    pointList = []
    length = len(sudoku)
    for i in range(length):
        if sudoku[i] == 0:
            p = point(i % 4,i // 4)    #遍历全部的点
            for j in range(1,5):       #找到不重复的结果
                if j not in rowNum(p,sudoku) and j not in colNum(p,sudoku):
                    p.available.append(j)
            pointList.append(p)
    return pointList


def tryInsert(p,sudoku):
    """
对于每一个须要的位置进行填充
:return:
    """
    availNum = p.available
    for v in availNum:
        p.value = v
        if check(p,sudoku):
            sudoku[p.y*4 + p.x] = p.value
            if len(pointList) <= 0:
                showSudoku(sudoku)
                exit()
            p2 = pointList.pop()
            tryInsert(p2,sudoku)
            sudoku[p2.y * 4 + p2.x] = 0
            sudoku[p.y * 4 + p.x] = 0
            p2.value = 0
            pointList.append(p2)
        else:
            pass


def check(p,sudoku):
    """
判断填充条件
:return:
    """ 
    #若是为0就不填充
    if p.value == 0:
        return False
    #再次检查填充的点是否与当前已经填充的点重复
    if p.value not in rowNum(p,sudoku) and p.value not in colNum(p,sudoku):
        return True
    else:
        return False


def showSudoku(sudoku):
    """
    定义输出格式
    """
    for j in range(4):
        for i in range(4):
            print('%d '%(sudoku[j * 4 + i]),end='')
        print('')


if __name__ == '__main__':
    sudoku = [
        4,0,0,0,
        0,0,0,1,
        0,0,1,0,
        0,0,3,4,
    ]
    pointList = initPoint(sudoku)
    p = pointList.pop()
    tryInsert(p,sudoku)

这是改完以后的,基本上都符合规范了,由于Pycharm在写的时候就会提示你的规范,因此改起来并无什么很大的难度。

性能分析

这显然对我来讲太难了,涉及到了个人知识盲区,我想不到更好的方法了,更好的方法我看不太懂,若是你真想看,请移步唐巍神仙的github去康康吧,对不起,我一个都不会。

单元测试

简单测试一下吧

import unittest
from sudoku import point

class SudokuTestCase(unittest.TestCase):
    def test_point(self):
        p = point(x=1, y=2)
        self.assertEqual(p.x,1)
        self.assertEqual(p.y,2)
        self.assertTrue(isinstance(p, point))


if __name__ == '__main__':
    unittest.main()

大工告成,别问我为何不写其余的单元测试,由于我懒。

后记

文件输入输出

我还有不少东西没写完,好比要求的是文件输入输出,可是我尚未了解Python对文件的操做,我不太会,我看看我这两天有没有空,有空地话再补上。

关于高宫格

个人以上代码是基于四宫格的,也适用于像三宫格、四宫格、五宫格、七宫格只考虑格而不考虑宫的数独,四宫格是一个特殊状况,由于结果是惟一的,虽然他有宫,可是并不用考虑。至六宫格、八宫格、九宫格还要考虑宫,而且宫里面也有不少种状况,我没有很好的思路,我个人脑细胞已经死完了,简单了解了一下好像很是麻烦,超过个人能力范围了。

总结

我不会编程,我不适合开发。

image.png

相关文章
相关标签/搜索