2020软件工程作业03

软件工程 2020软件工程作业
作业要求 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494
作业目的 实现一个命令行程序,不妨称之为Sudoku
作业正文 如下
其他参考文献 https://www.runoob.com/python3/python3-tutorial.html
https://github.com/changorz/work/tree/master/20177583/src
https://blog.csdn.net/qq_40871196/article/details/89431987

Github项目地址

https://github.com/zxw0621/demo/tree/master/20177596/src

PSP表格

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

思路描述


审题

  1. 按照数独游戏的规定,给一张含有若干的数字随机分布在网格的图,在每个位置上的数在列和行上不能重复出现,在4、6、8、9阶的数独表中,每个小宫格内不能有重复的数出现。
  2. 用cmd命令行的形式读出写入文件,并传入参数。
  3. 要实现多个九宫格一起处理,且解答情况唯一。
  4. 要求用java和c++,这里顺便学习下python(从大一刚学那会就学习过一点点,后来忙于学习学校安排的课程后没怎么学了,以后还想接触些脍炙人口的新鲜事物,比如某些大佬开口就是什么大数据,云计算,人工智能的)

功能模块设计

流程图

1.定位模块

def new_locate(_x_, _y_, _a_, _b_):  #上面的版本是学习小邓同学的定位
    '''
    新版定位
    :param _x_:
    :param _y_:
    :return:
    '''
    if M % 3 == 0:   #通过向下取整计算出 大的九宫格能分成几行几列的小九宫格
        _c_ = 3
        _r_ = int(M / 3)
    elif M % 2 == 0:
        _c_ = 2
        _r_ = int(M / 2)
    _a_ = int(_x_ // _c_ * _c_)
    _b_ = int(_y_ // _r_ * _r_)c_:_r_
    return _a_, _b_, _c_, _r_   #这里返回 _a_: 当前元素所在的小九宫格 左上角的横坐标
                               #         _b_: 当前元素所在的小九宫格 左上角的纵坐标
                               #         _c_: 大九宫格被分成了 r行 c列的小九宫格
                               #         _r_:


# def locate(_x_, _y_, _a_, _b_):     #莽夫判断  respect阿传
#     '''
#     确定当前元素所在九宫格位置
#     :param _x_:
#     :param _y_:
#     :param a:
#     :param b:
#     :return:
#     '''
#     if 0 <= _x_ < 3 and 0 <= _y_ < 3:
#         _a_ = 0
#         _b_ = 0
#
#     if 3 <= _x_ < 6 and 0 <= _y_ < 3:
#         _a_ = 3
#         _b_ = 0
#
#     if 6 <= _x_ < 9 and 0 <= _y_ < 3:
#         _a_ = 6
#         _b_ = 0
#
#     if 0 <= _x_ < 3 and 3 <= _y_ < 6:
#         _a_ = 0
#         _b_ = 3
#
#     if 0 <= _x_ < 3 and 6 <= _y_ < 9:
#         _a_ = 0
#         _b_ = 6
#
#     if 3 <= _x_ < 6 and 3 <= _y_ < 6:
#         _a_ = 3
#         _b_ = 3
#
#     if 3 <= _x_ < 6 and 6 <= _y_ < 9:
#         _a_ = 3
#         _b_ = 6
#
#     if 6 <= _x_ < 9 and 3 <= _y_ < 6:
#         _a_ = 6
#         _b_ = 3
#
#     if 6 <= _x_ < 9 and 6 <= _y_ < 9:
#         _a_ = 6
#         _b_ = 6
#     return _a_, _b_

2.行列无重复判断模块

def fun(_x_, _y_):
    '''
    确定行列上不重复
    :param inde__x_:
    :param _y_:
    :param num:
    :return:
    '''

    for _i_ in range(M):
        if _i_ != _x_ and DATA[_i_][_y_] == DATA[_x_][_y_]:#遍历行
            return 0
    for j in range(M):
        if j != _y_ and DATA[_x_][j] == DATA[_x_][_y_]:#遍历列
            return 0
    _a_ = 0
    _b_ = 0
    _a_, _b_, _c_, _r_ = new_locate(_x_, _y_, _a_, _b_) 
    for _i_ in range(_a_, _a_ + _c_):
        for j in range(_b_, _b_ + _r_):
            if _i_ != _x_ and j != _y_ and DATA[_i_][j] == DATA[_x_][_y_]:#遍历小九宫格
                return 0
    return 1

3.深度优先搜索模块

def dfs(_x_, _y_):
    '''
    深搜
    :param _x_:
    :param _y_:
    :return:
    '''
#这里就相当于给没填数字的格子 尝试可能填的数
    if _x_ > M - 1:
        disp()
    elif DATA[_x_][_y_] != 0:
        if _y_ == M - 1:
            dfs(_x_ + 1, 0)
        else:
            dfs(_x_, _y_ + 1)
    else:
        for _i_ in range(1, M + 1):
            DATA[_x_][_y_] = _i_  #从1到M 赋值尝试
            if fun(_x_, _y_) == 1:
                if _y_ == M - 1:
                    dfs(_x_ + 1, 0)
                else:
                    dfs(_x_, _y_ + 1)
        DATA[_x_][_y_] = 0  #回溯

4.文件的写入读出

for line in FP.readlines(): #读出
    arr = line.strip().split(" ")
    if arr[0] >= '0':
        int_arr = list(map(int, arr))
        DATA.append(int_arr)
#分行读出 每隔一个空格读一个数  map函数用于将读取到的内容转int整型 保存成列表数组List
写入有个write函数就完事了

5.命令行输入

python命令行输入用 import sys
 然后用sys.argv[x]数组输入
sys.argv[0]表示的是你的py文件名   用sys.argv[1......]  按你输入的顺序赋值给你要的参数变量

命令行输入图片

性能改进


改进了定位模块 上文有提到 实现3-9阶九宫格都可以定位

心路历程


一拿到题目的时候,我是拒绝的。彭琛老师发布的作业也太快了,妈妈再也不用担心我手指不灵活了,妈妈再也不用担心我会出门花钱了,妈妈再也不用担心洗发水用的完了,这算法 题,我裂开,这怎么做,算法我学了啥。大一:彭琛老师教给我们冒泡排序法。大二:数据结构课上,冒泡排序法,我一气呵成,老师说我这发量还不行。大三:算法课上,各种指针嵌套递归回溯,一到实验课,冒泡排序法信手捏来。预测大四:《论冒泡排序法的基本思想》朱旭炜20177596.doc。其实没有老师发布的作业,我真不会接触算法这些东西,既然发布了,还是先端正下态度哈。完成代码编写的过程也是十分艰辛,在CSDN上也看了一些其他大佬的实现思路,结合作业的要求完成。

​ 关于《构建之法》:

下次我一定看! 滑稽

单元测试


import unittest

from sudoku import new_locate

class MyTestCase(unittest.TestCase):
    def test_fun(self):
        test_num=new_locate(5,8,0,0)
        self.assertEqual(test_num,(3,6,3,3))


if __name__ == '__main__':
    unittest.main()
#计算新定位模块是否能正常计算出_a_,_b_,_c_,_r_

静态检测(工具:PyLint)


​ 刚完成的代码那会,点pylint,出来好多啊,结合上次作业出现的问题总结下:

  1. 写完代码前,点击Code - > Reformat Code, 在pycharm最上面那条找得到,这个效果是整理代码,点完这个都能消除很多提醒和警告
  2. 变量命名的方法,提醒你大写你就全大写,提醒snake命名就写成_name_ 像这样的,当然我也不知道这样规范不,请大佬教育下我
  3. def函数块里面要写注释,这个提醒也太人性化了,怕别人看你的代码时候看不懂,不然就C0116提醒了,这个搞了我好久,专门折磨我这种强迫症,类似的不写注释还有C0114,写在最上面就好了,告诉别人你这整个代码干嘛用的
  4. 还有就是要简化 i f 判断,特别是多个并列条件的判断
  5. 还有减少else后面不必要的return ,不要写else: 这样的,第一个判断里面有return的话,程序都出去了,没必要再else:return return x
  6. 暂无其他问题了,以后碰到会继续补充,也请大佬们指指点点下

总结


​ 虽然没做过算法题,通过自己查资料,咨询大佬后还是对问题的解答有所思路的,python挺好用的。希望彭琛老师发布作业的速度可以慢点,顶不住,早点开学吧,我太难了。

giao

大佬の指指点点


  • @不负真人:你这函数没引入DATA可以直接操作DATA的值吗?
def dfs(_x_, _y_):
    '''
    深搜
    :param _x_:
    :param _y_:
    :return:
    '''
#这里就相当于给没填数字的格子 尝试可能填的数
    if _x_ > M - 1:
        disp()
    elif DATA[_x_][_y_] != 0:  # < - 为啥能直接用?
        if _y_ == M - 1:
            dfs(_x_ + 1, 0)
        else:
            dfs(_x_, _y_ + 1)
    else:
        for _i_ in range(1, M + 1):
            DATA[_x_][_y_] = _i_  #从1到M 赋值尝试
            if fun(_x_, _y_) == 1:
                if _y_ == M - 1:
                    dfs(_x_ + 1, 0)
                else:
                    dfs(_x_, _y_ + 1)
        DATA[_x_][_y_] = 0  #回溯

博主亲自给这位爷查阅资料,寻找一些不会让自己尴尬且强有力的论证,在python语言中:

  1. 在函数中对全局变量只是进行引用而不是进行修改时,不需要使用global关键字

  2. 修改全局变量,需要使用global声明

  3. 列表、字典等如果只是修改其中元素的值,可以直接使用全局变量,不需要global声明

    参考文献:https://www.cnblogs.com/yymn/p/11299805.html

  • @不负真人:你这个定位m=5怎么办?问题是你好像没排除啊!没排除这几个情况!c和r都没有不会报错吗?没进到这里面,c和r没有值啊?是没有c和r吧,这会返回0?
def new_locate(_x_, _y_, _a_, _b_):  #上面的版本是学习小邓同学的定位
    '''
    新版定位
    :param _x_:
    :param _y_:
    :return:
    '''
    if M % 3 == 0:   #通过向下取整计算出 大的九宫格能分成几行几列的小九宫格
        _c_ = 3
        _r_ = int(M / 3)
    elif M % 2 == 0:
        _c_ = 2
        _r_ = int(M / 2)
    _a_ = int(_x_ // _c_ * _c_)
    _b_ = int(_y_ // _r_ * _r_)c_:_r_
    return _a_, _b_, _c_, _r_   #这里返回 _a_: 当前元素所在的小九宫格 左上角的横坐标
                               #         _b_: 当前元素所在的小九宫格 左上角的纵坐标
                               #         _c_: 大九宫格被分成了 c行 r列的小九宫格
                               #         _r_:

我:应该会返回0吧,你先试试。表面嘴硬,实则来一波单元测试。

import unittest
from sudoku import new_locate

class MyTestCase(unittest.TestCase):
    def test_something(self):
        test_num=new_locate(0,0,0,0)  #M=3 or 5 or 7
        self.assertEqual(test_num,(0,0,0,0) )


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

pytest: ZeroDivisionError: integer division or modulo by zero(你除0的样子真下饭)

改进:

def new_locate(_x_, _y_, _a_, _b_):
    '''
    新版定位
    :param _x_:
    :param _y_:
    :return:
    '''
    _c_ = 0 #< - 提前赋值0
    _r_ = 0
    if 105 % M != 0:  这个里是如果M不等于3、5、7,根据题意不考虑
        if M % 3 == 0:
            _c_ = 3
            _r_ = int(M / 3)
        elif M % 2 == 0:
            _c_ = 2
            _r_ = int(M / 2)
        _a_ = int(_x_ // _c_ * _c_)
        _b_ = int(_y_ // _r_ * _r_)
    return _a_, _b_, _c_, _r_ 这里如果M=3 or 5 or 7,返回 0 0 0 0
  • @不负真人:你的不行,你怕是没看题目要求吧!

image-20200321211332931

对比下我的:image-20200321211415597

img

我又开始上网冲浪,查阅资料image-20200321211900151

python命令行输入,输入参数时参数名做参数古被识别为参数做参数名 ???

还是改下吧

NAME_M = sys.argv[1]
NAME_N = sys.argv[3]
N = int(sys.argv[4])
M = int(sys.argv[2])
NAME_IN_FILE = sys.argv[5]
NAME_OUT_FILE = sys.argv[7]
IN_FILE = sys.argv[6]
OUT_FILE = sys.argv[8]


完事儿

参考文献:https://www.cnblogs.com/ouyangpeng/p/8537616.html

感谢@不负真人大佬的指指点点

大佬:我是fw[1]

后期修改


def dfs(_x_, _y_):
    '''
    深搜
    :param _x_:
    :param _y_:
    :return:
    '''
    global F  #加入F判断值  每个九宫格答案唯一  为最先找到得解答情况
    if _x_ > M - 1 and F == 0:
        disp()
        F = 1
    elif DATA[_x_][_y_] != 0:
        if _y_ == M - 1:
            dfs(_x_ + 1, 0)
        else:
            dfs(_x_, _y_ + 1)
    else:
        for _i_ in range(1, M + 1):
            DATA[_x_][_y_] = _i_
            if fun(_x_, _y_) == 1:
                if _y_ == M - 1:
                    dfs(_x_ + 1, 0)
                else:
                    dfs(_x_, _y_ + 1)
        DATA[_x_][_y_] = 0

for i in range(N):
    if i > 0:
        DATA[M * (i - 1):M * i - 1] = DATA[M * i:M * (i + 1)]
        print('')
        OP.write('\n')
    dfs(0, 0)
    F = 0  #在解答另外一个九宫格时 先初始化判断值

各阶宫格


3 × 3

image-20200328143008320

4 × 4

image-20200328143110983

5 × 5

image-20200328143222917

6 × 6

image-20200328143255130

7 × 7

image-20200328143327595

8 × 8

image-20200328143352792

9 × 9

image-20200328143426371

自评分


QQ截图20200329163310

纠错


    _c_ = 0
    _r_ = 0
    if 105 % M != 0:
        if M % 3 == 0:
            _r_ = 3             #<-行列混淆了 导致6,8阶错误
            _c_ = int(M / 3)
        elif M % 2 == 0:
            _r_ = 2
            _c_ = int(M / 2)
        _a_ = int(_x_ // _c_ * _c_)
        _b_ = int(_y_ // _r_ * _r_)
    return _a_, _b_, _c_, _r_

改正后:

6 × 6

image-20200405142233745

8 × 8

image-20200405142149482


  1. 来自大佬的自嘲,自称废物。 ↩︎

posted @ 2020-03-20 08:44  波兰杜兰特  阅读(711)  评论(11编辑  收藏  举报
Live2D