日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[软件工程基础]结对项目 数独程序扩展

發布時間:2025/3/18 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [软件工程基础]结对项目 数独程序扩展 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(1)在文章開頭給出Github項目地址。(1')

項目地址:https://github.com/JerryYouxin/sudoku

(2)在開始實現程序之前,在下述PSP表格記錄下你估計將在程序的各個模塊的開發上耗費的時間。(0.5')

PSP2.1Personal Software Process Stages預估耗時(分鐘)實際耗時(分鐘)
Planning計劃3060
· Estimate· 估計這個任務需要多少時間3030
Development開發10201540
· Analysis· 需求分析 (包括學習新技術)120240
· Design Spec· 生成設計文檔3040
· Design Review· 設計復審 (和同事審核設計文檔)60120
· Coding Standard· 代碼規范 (為目前的開發制定合適的規范)3060
· Design· 具體設計6090
· Coding· 具體編碼600800
· Code Review· 代碼復審6090
· Test· 測試(自我測試,修改代碼,提交修改)60100
Reporting報告5080
· Test Report· 測試報告50100
· Size Measurement· 計算工作量510
· Postmortem & Process Improvement Plan ·事后總結, 并提出過程改進計劃3030
合計12151770

(3)看教科書和其它資料中關于Information Hiding, Interface Design, Loose Coupling的章節,說明你們在結對編程中是如何利用這些方法對接口進行設計的。(5')

信息隱藏,對于面向對象的程序設計而言,信息隱藏對于將對象封裝,避免外來操作對其進行盲目乃至錯誤的操作,在具體實施上主要是通過只提供接口讀入和得到結果,避免其中的具體實現的過程對外暴露,從而導致不安全的訪問。我們在合作代碼的時候,盡量只給互相提供相互之間所需要的接口,避免因為看到內部代碼而導致的過度操作,以提高程序的安全性與健壯性。
interface design 和loose coupling實際上在很大程度上提高了我們工作的效率。這兩者都使我們能夠專注于自身所要完成的那一部分代碼中,使得個人的工作具體化,簡單化,而不要求我們必須具有著眼整個完整項目的能力,也避免了我們在擁有這種能力之前過于好高騖遠或者不敢下手,從而浪費時間,松耦合和接口設計使得我們能夠專心做好自己的事情,不必過度分心從而導致的效率下降,同時,只提供接口和松耦合的方式也使得我們彼此的代碼不互相干擾,從而導致雙方共同的困難。

4)計算模塊接口的設計與實現過程

計算模塊接口的設計與實現過程
計算模塊接口如下(Core類中的成員函數):

void generate(int number,int mode,int result[][81]);void generate(int number,int lower,int upper,bool unique,int result[][81]);void generate(int number,int result[][81]); // generate final statesbool solve(int puzzle[],int solution[]);void solve(int number,int *puzzle,int *solution);int read_sudoku(int **puzzle,const char* filename);int write_sudoku(int number,int *puzzle,const char* filename);bool check_valid(int *solution);int check_valid(int number,int *solution);bool check_same(int number,int *solution);

其中generate(int number, int result)用回溯法進行終局的搜索(可以參考我的個人項目的博客)。在實現生成只有唯一解的數獨時,先用上述終局生成函數生成數獨終局,再進行挖空,每次挖空后用解數獨函數來求解數獨,該函數可以返回數獨解的數量,由此可以判斷是否為唯一解。如果不是唯一解的數獨則回溯不挖空,并再查看下一個空是否可以挖(這是一個遞歸)。代碼如下:

bool Core::__generate_unique(int num, int maxNum, int index, int result[]) {if(index>=81||maxNum<=num) return maxNum<=num;int x = result[index];result[index] = 0;if(isUniqueSolve(result)) {return __generate_unique(num+1,maxNum,index+1,result);}else {result[index] = x;return __generate_unique(num,maxNum,index+1,result);} }void Core::generate(int number,int lower,int upper,bool unique,int result[][81]) {if(number<1||number>10000) throw NumException();if(lower>upper||lower<0||upper>64) throw RangeException();if(unique) {generate(number,result); #pragma omp parallel forfor(int n=0;n<number;++n) {int num = lower;//rand()%(upper-lower+1)+lower;__generate_unique(0,num,0,result[n]);}} else {generate(number,result);for(int n=0;n<number;++n) {int num = rand()%(upper-lower+1)+lower;for(int i=0;i<81;++i) {if(81-i+1<=num) {result[n][i] = 0;--num;}else {double r = rand()/(double)RAND_MAX;if(r<0.5) {result[n][i] = 0;--num;}}}}} }

在檢查函數上,Core類提供了三個接口以供檢查數獨合法性等信息:

// check functions bool check_valid(int *solution); int check_valid(int number,int *solution); bool check_same(int number,int *solution);

其中check_valid為檢查輸入數獨是否為合法數獨,即每個數字都是0~9之間的整數且符合數獨的規則要求。如果是合法的bool check_valid(int)會返回true,int check_valid(int,int)會返回0,否則為非法數獨。其中bool類型的需要保證輸入的數獨是只有一個的,且所有的函數輸入都至少分配好足夠的內存空間。
我實現了其中的-m生成部分。
我的-m生成難度的是基于挖的空的數量和空間自由度兩個方面決定。
一般而言,肯定是挖空越多總體趨勢上難度越大。

難度等級的增加,空格數總體趨勢遞增,不同難度等級的題目空格數也一樣的情況。我們得出初步結論,簡單按照空格的數目多少來劃分數獨題目的難易程度是不全面的。同樣空格數的數獨題目,空格數分布位置的不同對難度等級也造成影響。
在這種思維下,我們提出自由度的概念:,數獨的難度等級與行、列、宮格內的空格數存在著聯系。提出以空格自由度衡量數獨的難度等級。數獨的空格自由度,指除掉空格本身,空格所在行、列、九宮內的空格數總和。

我們通過這兩個方面的思維,將空格從20到55分為3個區間,自由度從700到1300分為三個區間,難度的劃分是只要一個條件達到了相應區間就可以認為達到了要求。
這樣我們可以在第一次個人項目的基礎上,隨即進行挖空,直至滿足了條件。
可以參考
http://www.cnblogs.com/candyhuang/archive/2011/12/21/2153668.html

(5)閱讀有關UML的內容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。畫出UML圖顯示計算模塊部分各個實體之間的關系(畫一個圖即可)。(2’)


UML圖
計算模塊接口部分的性能改進
原來的回溯算法性能很低,所以在求解上,由個人項目時的回溯法改為了快速求解精確覆蓋問題的DLX算法。但是在實現后卻發現加速并不明顯,甚至還有些慢。在最耗時間的生成唯一解的情況下,用VS性能分析后得到如下圖:

[1]!(http://images2017.cnblogs.com/blog/1224149/201710/1224149-20171015145603137-289738865.png)

由于我截圖的時候代碼有些改動導致函數名并不能正常顯示。圖中最耗時間的是DLXSolver中的solve函數,由于查找唯一解時挖空是遞歸搜索,所以這是很正常的現象。但是注意到第二位的那個new函數,是分配空間的函數,這個耗時也很長,這是為什么?看看代碼,發現在將數獨問題轉換為精準覆蓋問題時會申請內存空間來建立雙向十字鏈表來存儲稀疏矩陣。由于原來的實現為一個節點一個節點進行內存分配,所以只要改為剛開始統一分配好一批節點后,后面按需所配即可。改進后速度有了大大提高.

計算模塊部分單元測試展示

單元測試思路為每個主要的函數都進行測試。I/O操作的單元測試,其中refRes是一個合法的數獨數組:

TEST_METHOD(TestRead){Core core;int refRes[]={...};int* result;core.read_sudoku(&result,"sudokuexp.txt");for(int i=0;i<162;++i)Assert::AreEqual(refRes[i],result[i]);delete[] result;}TEST_METHOD(TestWrite){Core core;int refRes[]={...};int* result;core.write_sudoku(2,refRes,"sudoku.txt");core.read_sudoku(&result,"sudoku.txt");for(int i=0;i<162;++i)Assert::AreEqual(refRes[i],result[i]);delete[] result;}

對于generate,對于每個不同大小的輸入、不同類型的接口進行測試,并在最后調用檢查函數check_valid和check_same來驗證正確性。

TEST_METHOD(TestGen...){Core core;const int N = ...; // MAXIMUM//int result[N][81];int* result = new int[N*81];core.generate(N,(int(*)[81])result);Assert::AreEqual(core.check_valid(N,(int*)result),0);Assert::IsTrue(core.check_same(N,(int*)result));delete[] result;}

Solve函數同樣,讀入測試數據后調用solve接口進行解題,然后用check_valid進行正確性檢查。

TEST_METHOD(TestSolveExp){Core core;const int N = 2; // MAXIMUMint* result = new int[N*81];//int result[N][81];int* puzzle;core.read_sudoku(&puzzle,"sudokuexp.txt");core.solve(N,puzzle,(int*)result);Assert::AreEqual(core.check_valid(N,(int*)result),0);Assert::IsTrue(core.check_same(N,(int*)result));delete[] result;delete[] puzzle;}

單元測試結果如下:

(7)看Design by Contract, Code Contract的內容:

http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述這些做法的優缺點, 說明你是如何把它們融入結對作業中的。(5')
這兩種做法指的都是約定性地進行代碼工作,兩個人實現就兩者要進行對接的函數的條件,參數條件(包括參數的取值范圍,類型),各自明確在什么條件下,接受什么條件的指令,不滿足條件的可以不接受,這種方式的優點在于能夠更好地敦促雙方按照實現的約定嚴格書寫代碼,提高的代碼的魯棒性和健壯性,有可能促成高水平工程的完成,但是缺點在于如果兩個人水平有一定差距的話,不利于兩個人更加方便地進行合作編程,會導致木桶效應的發生。

計算模塊部分異常處理說明

計算模塊異常有四類:RangeException,NumException,ModeException,InvalidSudokuException。每個異常都在函數剛開始進行參數檢查時,如果參數異常則拋出相關異常。單元測試如下,以下面為例:

TEST_METHOD(TestInvalidSudokuException){Core core;const int N = 1; // MAXIMUMint puzzle[] ={9, 9, 8, 0, 6, 0, 1, 2, 4,2, 3, 7, 4, 5, 1, 9, 6, 8,1, 4, 6, 0, 2, 0, 3, 5, 7,0, 1, 2, 0, 7, 0, 5, 9, 3,0, 7, 3, 0, 1, 0, 4, 8, 2,4, 8, 0, 0, 0, 5, 6, 0, 1,7, 0, 4, 5, 9, 0, 8, 1, 6,8, 1, 0, 7, 4, 6, 2, 0, 0,3, 0, 5, 0, 8, 0, 7, 0, 9,};int result[81]={ 0 };try {Assert::IsFalse(core.check_valid(puzzle));Assert::AreEqual(core.check_valid(1,puzzle),1);core.solve(puzzle,(int*)result);Assert::Fail();}catch(InvalidSudokuException e) {}}

(10)界面模塊的詳細設計過程。在博客中詳細介紹界面模塊是如何設計的,并寫一些必要的代碼說明解釋實現過程。(5')

首先是一個計時的工具,通過Qlabel來實現,在時間能夠走得情況下(通過全局變量changable來控制),將time_show增長,并且將其的分鐘數和秒數set到兩個label的txt上面,就是先了記時的功能。

void MainWindow::time_change() {if(time_changable==true){time_show++;int minute=time_show/60;int second=time_show%60;char str1[3];char str2[3];sprintf(str1,"%02d",minute);sprintf(str2,"%02d",second);minute_->setText(str1);second_->setText(str2);}

接下來是各個控制按鈕,它們的類型都是QPushButton,它們之間有一定的邏輯關系,比如在剛剛按下new_game按鈕的時候,除了restart和new_game之外的按鈕都是不能按下的,而一旦選擇了數獨中的按鈕,那么根據選擇的是原先就有的,新填上的還是現在為止還沒有填上的,不同的按鈕會分別轉換為可選中和不可選中的狀態,以下面的代碼為例

void MainWindow::pause() {if(Pause->text().compare("Pause")==0){time_changable=false;for(int i=0;i<81;i++){follow[i]->setEnabled(false);}for(int j=0;j<9;j++){fillin[j]->setEnabled(false);}Remind_Me->setEnabled(false);Check->setEnabled(false);Wipe->setEnabled(false);Pause->setText("Continue");}else{if(Pause->text().compare("Continue")==0){time_changable=true;for(int i=0;i<81;i++){follow[i]->setEnabled(true);}Remind_Me->setEnabled(true);Check->setEnabled(true);Wipe->setEnabled(true);Pause->setText("Pause");}}for(int j=0;j<9;j++){fillin[j]->setEnabled(false);} } void MainWindow::wipe() {QString str1=last_button->objectName();Wipe->setEnabled(false);QString tmp;for(int j = 0; j < str1.length(); j++){if(str1[j] >= '0' && str1[j] <= '9')tmp.append(str1[j]);}bool ok;int index=tmp.toInt(&ok,10);if((last_button->text().compare("")!=0)&&sudukuinto[index]==0){follow[index]->setText("");nowsuduku[index]=0;}for(int j=0;j<9;j++){fillin[j]->setEnabled(false);} }

最后的成品大概是這樣的,可以計時、選擇難度、開新局,重新此局,刪除填過的空。

(11)界面模塊與計算模塊的對接。詳細地描述UI模塊的設計與兩個模塊的對接,并在博客中截圖實現的功能。(4')

通過與難度生成(-m),求解的函數對接來實現UI模塊求解的全過程。

void Core::generate(int number,int mode,int result[][81]) {if(number<1||number>10000) throw NumException();if(mode!=1&&mode!=2&&mode!=3) throw ModeException();int i = 0;int free_degree = 0;int sudoku_num = 0;int num = 0;int the_time = 0;int hollow_num = 0;for (sudoku_num = 0; sudoku_num < number; sudoku_num++){generate(1,(int(*)[81])result[sudoku_num]);free_degree = 0;num = 0;if (mode == 1){hollow_num=rand() % 9 + 40;if (the_time == 0){for (int ii = 0;; ii = ii + 2){

上面這部分是實現難度生成的代碼,而這部分的代碼通過什么樣的方式與UI的工程相聯系起來呢,主要是通過動態鏈接庫所生成的代碼庫,UI通過調用來實現的。在UI的顯示類中將core定義為其中的一個屬性,然后通過調用生成和求解的方法將相應的結果返回到UI模塊的一個變量result中,再通過UI的settext將其顯示在圖形界面上。
具體代碼思想大概是這樣:

private:QPushButton *follow[81];QPushButton *fillin[10];QPushButton *Remind_Me;QPushButton *Restart;QPushButton *New_Game;QPushButton *Check;QPushButton *Pause;QPushButton *Wipe;QComboBox *choose_;QLabel *minute_;QLabel *second_;Core core;core.generate(1,diff,result[i]);QString mode=ui->centralWidget->findChild<QComboBox*>("choose")->currentText();bool a=false;switch( QMessageBox::question(this, "begin a new game?","you choose "+mode+" ,are you sure"),QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Ok )

(12)描述結對的過程,提供非擺拍的兩人在討論的結對照片。(1')

(13)看教科書和其它參考書,網站中關于結對編程的章節,例如:

http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
說明結對編程的優點和缺點。
結對的每一個人的優點和缺點在哪里 (要列出至少三個優點和一個缺點)。(5')
結對編程的優點在于兩個人之間可以互相合作著編程,可以避免很多平時個人編程中所出現的錯誤,但是因為結對編程必然要以一個人的代碼為主要模板,這就導致另一個人在參與的過程中會有一些生疏和不習慣,比較多的時間都用來理解對方的代碼,其次,則是合作編程在時間上有時候不太好掌握,有時候會導致進度不統一,一個人早做完而另一個一直在做的情況也可能出現。
同伴優點:代碼能力出眾,軟工大佬;代碼知識較為廣泛,能夠解決很多以前沒有接觸過方面的問題,大大加快了最后將前端和后端融合的進度;對于測試方面有足夠的知識,對于程序的正確性方面做出了很大貢獻。
缺點:缺乏足夠合理的溝通導致進度不夠統一,影響了效率。

轉載于:https://www.cnblogs.com/zhj233/p/7671035.html

總結

以上是生活随笔為你收集整理的[软件工程基础]结对项目 数独程序扩展的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。