2020軟件工程做業03

這個做業屬於那個課程 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1
這個做業的要求在哪裏 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494
這個做業的目標 實現一個命令行程序,不妨稱之爲Sudoku。
做業正文 http://www.javashuo.com/article/p-ppswwxjn-dr.html
其餘參考文獻 www.baidu.com
http://www.javashuo.com/article/p-rwufmuum-dd.html
https://blog.csdn.net/sunyanxiong123/article/details/76401590
https://github.com/zxw0621/demo/blob/master/20177596/src/sudoku.py

1、GitHub url:https://github.com/liutaodashuaige/LT_DEMO/tree/master/20177569

2、PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30 100
Estimate 估計這個任務須要多少時間 20 30
Development 開發 800 1000
Analysis 需求分析 (包括學習新技術) 60 120
Design Spec 生成設計文檔 30 20
Design Review 設計複審 20 10
Coding Standard 代碼規範 (爲目前的開發制定合適的規範) 30 30
Design 具體設計 30 60
Coding 具體編碼 200 250
Code Review 代碼複審 30 30
Test 測試(自我測試,修改代碼,提交修改) 60 200
Reporting 報告 60 150
Test Repor 測試報告 10 30
Size Measurement 計算工做量 20 -
Postmortem & Process Improvement Plan 過後總結, 並提出過程改進計劃 30 120
合計 890 1250


3、解題思路

1.理解問題

  • 該算法題的需求是實現一個稱之爲Sudoku命令行程序。
  • 程序要實現利用邏輯和推理,在在數獨盤面的空格上填入1-9的數字。使1-9每一個數字在每一行、每一列和每一宮中都只出現一次。
  • 輸入要求輸入文件名以命令行參數傳入。
  • 輸出要求輸出n個程序解出的盤面,每兩個盤面間空一行,每一個盤面中,每兩個小格之間有一個空格。

2.思考如何實現

  • 因爲不一樣階的數獨盤面雖然空格數量上有差別,但對每個空格合法性的判斷方法和區塊的構造都是類似的,同時這是一個查找最優策略的問題,所以我認爲應該使用遞歸的方式解題。

3.尋找參考資料

4、設計實現過程

1.函數模塊的設計

  • 根據本題的解題要求和思考中肯定的遞歸解題思想,應設計如下模塊:
    • 主函數模塊
    • 遞歸查找模塊
    • 合法性判斷模塊
    • 輸出文件生成模塊

2.具體功能函數設計

  • main(argv)函數html

    • 接受命令行參數,並進行解析
    • 讀取input文件,並進行解析
    • 根據盤面數量調用DFS()深度優先搜索遞歸函數
  • DFS(i, x, y)函數java

    • 對當前遞歸狀態進行判斷
    • 對當前座標格狀態進行判斷
    • 嘗試填入數值,並調用judge()函數驗證其合法性
    • 根據不一樣條件進行遞歸
    • 驗證失敗時,回溯
  • judge(i, x, y)函數git

    • 對傳入的座標進行行列重複判斷
    • 對傳入的座標進行區塊定位
    • 對傳入的座標進行區塊重複判斷
    • 根據是否合法返回布爾值
  • MY_OTP(i)函數github

    • 根據傳入的盤面序號將該盤面矩陣寫入output文件
  • 簡單流程圖
    算法

3.全局變量設置

  • M(盤面階數)
  • N(盤面數目)
  • MY_MAPS(儲存全部盤面矩陣的三維列表)
  • op(文件對象)

5、改進思路

1.代碼靜態分析

  • 首先咱們使用pylint進行代碼靜態分析

很明顯代碼已是炸了,不過比起第一次用pylint已經好很多了...第一次但是負分XD數組

  • 讓PyCharm幫我整理一下...

看下效果,有所進步,剩下的就是命名的規範了
性能優化

  • 修改不符合規範的命名後

通過一陣搗鼓評分提高了很多,但仍然沒有達到滿分,測試了一下代碼正常運行
只能說,有時候投降不失爲一種優雅的退場,我就不折磨本身了

網絡


2.代碼性能優化

#改成直接給予參數,而不是從命令行接受
if __name__ == '__main__':
    # sys.argv[1:]爲要處理的參數列表,sys.argv[0]爲腳本名,所以棄之不用
    #main(sys.argv[1:])
    main(['-m', '9', '-n', '2', '-i', 'input.txt', '-o', 'output.txt'])
  • 利用PyCharm的profile進行代碼性能分析

顯然對於我來講這個圖是天書

app


由此表中可得知被調用次數最多和耗時最多的是judge()合法性判斷函數和_DFS_()遞歸函數框架

  • 性能優化集中於judge()和_DFS_()兩個函數

3.單元測試

  • 函數judge()有返回值,而且是一個布爾值,對該函數進行單元測試
  • 卡了好久,一直沒法導入py文件的函數,經過參考資料中的方法解決了
  • 涉及到全局變量,先對judge()函數進行一些調整,添加一段代碼
M = 9
    MY_MAPS = []
    # 上面都是給judge()函數運行提供必要的全局變量
    with open('output.txt', 'r', encoding='utf-8') as _fp_:  # 此處直接讀取已解矩陣用來判斷合法性
        _MYMAP_ = []
        for line in _fp_.readlines():
            if line != '\n':  # 用換行符分割矩陣
                _MYMAP_.append(list(map(int, line.strip().split(" "))))
            else:
                MY_MAPS.append(_MYMAP_)
                _MYMAP_ = []
        MY_MAPS.append(_MYMAP_)  # MY_MAPS是集合了全部數據的三維數組
    #單元測試時用來提供全局變量...

    #################
    #global M, MY_MAPS
    # 行列不重複判斷
  • 測試代碼編寫
import unittest
from Sudoku import judge

class test_judge(unittest.TestCase):
    def test_myfun(self):
        test_num = judge(0, 1, 2)#測試數值
        self.assertEqual(test_num, 1)#指望值


if __name__ == '__main__':
    unittest.main()
  • 測試結果

GOOOOOD!測試符合預期結構

4.對代碼全面檢測和優化後,更新GitHub上的倉庫

6、代碼說明

0.導包和定義全局變量

# -*- coding: UTF-8 -*-
import sys
import getopt

# 全局變量
M = ""
N = ""
MY_MAPS = []
OP = ""

1.主函數

if __name__ == '__main__':
    # sys.argv[1:]爲要處理的參數列表,sys.argv[0]爲腳本名,所以棄之不用
    main(sys.argv[1:])

2.main()函數

def main(argv):
    """
    經過sys模塊來識別參數
    :return:
    """
    # 聲明全局變量
    global M, N
    global MY_MAPS, OP
    in_put = ""
    out_put = ""
    try:  # 獲取參數並處理異常
        opts, args = getopt.getopt(argv, "m:n:i:o:", ["help"])
    except getopt.GetoptError:
        print('Error: Sudoku.py -m -n -i -o')
        sys.exit(2)
    # 處理獲取的參數
    for opt, arg in opts:
        if opt in "--help":  # 給予幫助提示
            print('Error: Sudoku.py -m -n -i -o')
            sys.exit()
        elif opt in "-m":
            M = int(arg)
        elif opt in "-n":
            N = int(arg)
        elif opt in "-i":
            in_put = arg
        elif opt in "-o":
            out_put = arg
    with open(in_put, 'r', encoding='utf-8') as _fp_:  # 以讀狀態打開指定文件讀取矩陣
        _MYMAP_ = []
        for line in _fp_.readlines():
            if line != '\n':  # 用換行符分割矩陣
                _MYMAP_.append(list(map(int, line.strip().split(" "))))
            else:
                MY_MAPS.append(_MYMAP_)
                _MYMAP_ = []
        MY_MAPS.append(_MYMAP_)  # MY_MAPS是集合了全部數據的三維數組

    OP = open(out_put, 'w', encoding='utf-8')

    for i in range(N):
        if i > 0:
            OP.write('\n')  # 分割矩陣
        _DFS_(i, 0, 0)  # 遞歸求解
    OP.close()

3.DFS()遞歸函數

def _DFS_(_i_, _x_, _y_):
    """
    【DFS】深度優先搜索遞歸方式
    :return:
    """
    # 聲明引用全局變量
    global M, MY_MAPS
    if _x_ > M - 1:  # 完成條件
        _MY_OTP_(_i_)  # 保存數值
    elif MY_MAPS[_i_][_x_][_y_] != 0:  # 當前格子不可填
        if _y_ == M - 1:  # 右邊界換行
            _DFS_(_i_, _x_ + 1, 0)
        else:
            _DFS_(_i_, _x_, _y_ + 1)  # 下一格
    else:  # 當前格可填
        for i in range(1, M + 1):
            MY_MAPS[_i_][_x_][_y_] = i  # 試探填入數值
            if judge(_i_, _x_, _y_):  # 判斷其試探值的合法性,當判斷函數返回值爲1即合法
                if _y_ == M - 1:  # 邊界狀況
                    _DFS_(_i_, _x_ + 1, 0)
                else:
                    _DFS_(_i_, _x_, _y_ + 1)
            # 回溯
            MY_MAPS[_i_][_x_][_y_] = 0

4.judge()合法性判斷函數

def judge(_i_, _x_, _y_):
    """
    合法性判斷
    :return:
    """
    global M, MY_MAPS
    # 行列不重複判斷
    for i in range(M):
        if i != _x_ and MY_MAPS[_i_][_x_][_y_] == MY_MAPS[_i_][i][_y_]:
            return 0
        if i != _y_ and MY_MAPS[_i_][_x_][_y_] == MY_MAPS[_i_][_x_][i]:
            return 0
    # 區塊重複判斷
    _x1_ = _y1_ = row = col = 0  # 塊內座標初始值
    # 區塊定位參考於https://github.com/zxw0621/demo/blob/master/20177596/src/sudoku.py#L42
    # 這定位寫的太好了
    # 根據其階數肯定其模塊規模以及所屬模塊
    if M % 3 == 0:
        row = 3
        col = int(M / 3)
    elif M % 2 == 0:
        row = 2
        col = int(M / 2)
    _x1_ = int(_x_ // row * row)
    _y1_ = int(_y_ // col * col)
    # 遍歷所屬區塊,檢查其合法性
    for i in range(_x1_, _x1_ + row):
        for j in range(_y1_, _y1_ + col):
            if _x_ != i and _y_ != j and MY_MAPS[_i_][_x_][_y_] == MY_MAPS[_i_][i][j]:
                return 0
    return 1

5.MY_OTP()數據儲存函數

def _MY_OTP_(_i_):
    """
    向文件內寫入所得矩陣
    :return:
    """
    global N, M, MY_MAPS, OP
    # 遍歷當前求解矩陣
    for _x_ in range(M):
        for _y_ in range(M):
            OP.write(str(MY_MAPS[_i_][_x_][_y_]) + ' ')
        OP.write('\n')  # 換行

6.異常處理

  • 當參數輸入異常時,輸出提示幫助輸入
try:  # 獲取參數並處理異常
        opts, args = getopt.getopt(argv, "m:n:i:o:", ["help"])
    except getopt.GetoptError:
        print('Error: Sudoku.py -m -n -i -o')
        sys.exit(2)
  • 當用戶在命令行輸入-help參數時,給予提示
# 處理獲取的參數
    for opt, arg in opts:
        if opt in "--help":  # 給予幫助提示
            print('Error: Sudoku.py -m -n -i -o')
            sys.exit()
        elif opt in "-m":
            M = int(arg)
        elif opt in "-n":
            N = int(arg)
        elif opt in "-i":
            in_put = arg
        elif opt in "-o":
            out_put = arg

7.代碼運行結果

命令行

輸入文件

輸出文件

至此程序宣佈完成...

7、心路歷程

記錄

  • 3月21日
    • 截至到12:00,目前仍然在研究代碼實現思路(拖,就硬拖)
    • psp表格
    • 研究DFS遞歸函數
    • 設計功能模塊
    • 查閱資料,實現命令行輸入參數
    • 完成main()函數代碼
    • 上傳項目到GitHub,被網絡折磨了半小時
    • 顯然急於求成是愚蠢的,折磨了本身一夜


  • 3月22日
    • 截至到12:00,完成了博客的基本框架
    • 完成所有代碼塊
    • 完成流程圖
    • 代碼靜態檢測、性能分析、單元測試(折磨王三連折磨)
    • 代碼優化,賊難受
    • 更新GitHub倉庫(還好這波網絡沒炸)
    • 寫博客
    • 終於完成了!!!蕪湖~
總結

——在本次任務中我深入的認識到了不能閉門造車的道理,死鑽牛角尖最後只會折磨本身,應該具備發散性的思惟,充分利用網絡資源查閱資料,從多個角度對問題進行分析。最終得出本身的解題思路。同時在工做學習時不能急於求成,這樣反而會使得本身的效率降低,得不償失。在遭遇挫折的時候不妨帶着問題和其餘人互相交流一番,或許問題會用迎刃而解。最後,事實證實寫代碼只佔做業的10%,一系列附屬操做把我給折磨的不輕...

相關文章
相關標籤/搜索