這個做業屬於哪一個課程 | 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
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對文件的操做,我不太會,我看看我這兩天有沒有空,有空地話再補上。
個人以上代碼是基於四宮格的,也適用於像三宮格、四宮格、五宮格、七宮格只考慮格而不考慮宮的數獨,四宮格是一個特殊狀況,由於結果是惟一的,雖然他有宮,可是並不用考慮。至六宮格、八宮格、九宮格還要考慮宮,而且宮裏面也有不少種狀況,我沒有很好的思路,我個人腦細胞已經死完了,簡單瞭解了一下好像很是麻煩,超過個人能力範圍了。
我不會編程,我不適合開發。