軟件工程第三次做業

這個做業屬於哪一個課程 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

相關文章
相關標籤/搜索