201971010223-劉溫元 實驗三 結對實驗 《{0-1}KP 例項資料集演算法實驗平臺》
阿新 • • 發佈:2022-04-04
專案 | 內容 |
---|---|
班級部落格連結 | 點選這裡 |
本次作業要求連結 | 點選這裡 |
我的課程學習目標 | (1)學習並掌握結對程式設計的相關知識。(2)通過實驗提高自己的開發能力。 |
結對對方學號-姓名 | 201971010150-閆雨馨 |
結對方本次部落格作業連結 | 閆雨馨的部落格 |
本專案Github的倉庫連結地址 | 劉溫元的倉庫 |
任務一:閱讀《現代軟體工程—構建之法》第3-4章內容,理解並掌握程式碼風格規範、程式碼設計規範、程式碼複審、結對程式設計概念
- 程式碼複審
- 程式碼複審的定義:看程式碼是否在程式碼規範的框架內正確地解決了問題。
- 目的
- 找出程式碼的錯誤。
- 發現邏輯錯誤。
- 發現演算法錯誤。
- 發現潛在的錯誤和迴歸性錯誤。
- 發現可能需要改進的錯誤。
- 教育(互相教育)開發人員,傳授經驗,讓更多的成員熟悉專案各部分的程式碼,同時熟悉和應用領域相關的實際知識。
- 步驟:
- 程式碼必須成功地編譯。
- 程式設計師必須測試過程式碼。
- 程式設計師必須提供新的程式碼,以及檔案差異分析工具。
- 在面對面的複審中,一般是開發者控制流程,講述修改的前因後果。但是複審者有權在任何時候打斷敘述,提出自己的意見。
- 複審者必須逐一提供反饋意見。
- 開發者必須負責讓所有的問題都得到滿意的解釋或解答,或者在TFS中建立新的工作項以確保這些問題會得到處理。
- 對於複審的結果,雙方必須達成一致的意見。
- 結對程式設計
- 好處:1.在開發層次,結對程式設計能提供更好的設計質量和程式碼質量,兩人合作解決問題的能力更強。兩人合作,還有相互激勵的作用,工程師看到別人的思路和技能,得到實時的講解,受到激勵,從而努力提高自己的水平,提出更多創意。
2.對開發人員自身來說,結對工作能帶來更多的信心,高質量的產出能帶來更高的滿足感。
3.在企業管理層次上,結對能更有效地交流,相互學習和傳遞經驗,分享知識,能更好地應對人員流動。 - 兩人合作的各個階段:萌芽階段 → 磨合階段 → 規範階段 → 創造階段 → 解體階段。
- 影響他人的方式:斷言,橋樑,說服,吸引。
- 角色:駕駛員:控制鍵盤輸入;領航員:起到領航、提醒的作用。
- 好處:1.在開發層次,結對程式設計能提供更好的設計質量和程式碼質量,兩人合作解決問題的能力更強。兩人合作,還有相互激勵的作用,工程師看到別人的思路和技能,得到實時的講解,受到激勵,從而努力提高自己的水平,提出更多創意。
任務二:兩兩自由結對,對結對方《實驗二 軟體工程個人專案》的專案成果進行評價。
結對方資訊:
結對方學號-姓名 | 201971010150-閆雨馨 |
---|---|
結對方部落格 | 實驗二部落格 |
結對方github地址 | 實驗二專案地址 |
- 部落格地址
- 博文結構:博文結構清晰,版式整潔,錯落有致,具有段落感。同時將一些重要的地方用高亮顯示了出來,具有觀看體驗。
- 博文內容:實驗內容完成度不錯,基本實現了實驗二中的要求,並對需求分析、功能設計及設計實現設計實現都進行了詳細的稱述。
- 博文結構與PSP中“任務內容”列的關係:博主的博文撰寫流程是按照PSP的主要流程一步一步走下來的,具有較好的整體構思。
- PSP資料的差異化分析與原因探究:博主實際完成時間遠大於計劃完成時間,主要是在具體編碼,程式碼複審與事後總結這三個階段。結合我自己的開發經驗,主要原因可能在於對PSP具體流程的不熟悉,自我估計不足等。也有可能是在開發過程中遇到了一些預料之外的突發情況等
程式碼核查表:
說明 | 內容 |
---|---|
程式碼符合需求和規格說明麼? | 基本符合,但部分功能完成度較差。 |
程式碼設計是否考慮周全? | 比較考慮周全 |
程式碼可讀性如何? | 未採用模組化程式設計,可讀性可以。 |
程式碼容易維護麼? | 不易維護 |
程式碼的每一行都執行並檢查過了嗎? | 是 |
設計規範部分 | |
設計是否遵從已知的設計模式或專案中常用的模式? | 沒有 |
有沒有硬編碼或字串/數字等存在? | 沒有,採用的都是符合命名規範的變數名 |
程式碼有沒有依賴於某一平臺,是否會影響將來的移植? | 沒有 |
開發者新寫的程式碼能否用已有的Library/SDK/Framework中的功能實現? | |
在本專案中是否存在類似的功能可以呼叫而不用全部重新實現? | 不知道 |
有沒有無用的程式碼可以清除?(很多人想保留儘可能多的程式碼,因為以後可能會用上,這樣導致程式檔案中有很多註釋掉的程式碼,這些程式碼都可以刪除,因為原始碼控制已經儲存了原來的老程式碼) | 有 |
程式碼規範部分 | |
修改的部分符合程式碼標準和風格麼? | 不符合 |
具體程式碼部分 | |
有沒有對錯誤進行處理? | |
對於呼叫的外部函式,是否檢查了返回值或處理了異常 | 沒有 |
引數傳遞有無錯誤,字串的長度是位元組的長度還是字元(可能是單/雙位元組)的長度,是以0開始計數還是以1開始計數? | 無錯誤;本專案中是以0開始計數。 |
有沒有使用斷言(Assert)來保證我們認為不變的條件真的得到滿足? | 沒有 |
對資源的利用是在哪裡申請,在哪裡釋放的? | |
有沒有可能導致資源洩露(記憶體、檔案、各種GUI資源、資料庫訪問的連線,等等)? | |
有沒有優化的空間? | 在記憶體中完成,有可能洩露 |
資料結構中有沒有用不到的元素? | 沒有 |
效能 | |
程式碼的效能(Performance)如何? | |
最壞的情況如何? | 基本達到要求 |
程式碼中,特別是迴圈中是否有明顯可優化的部分(C++中反覆建立類,C#中 string 的操作是否能用StringBuilder 來優化)? | 有 |
對於系統和網路呼叫是否會超時? | |
如何處理? | 無相關功能 |
可讀性 | |
程式碼可讀性如何? | |
有沒有足夠的註釋? | 程式碼註釋較少,閱讀比較困難 |
可測試性 | |
程式碼是否需要更新或建立新的單元測試? | |
針對特定領域的開發(如資料庫、網頁、多執行緒等),可以整理專門的核查表 | 需要 |
任務三完成情況說明:
PSP流程:
PSP2.1 | 任務內容 | 計劃完成需要的時間(min) | 實際完成需要的時間(min) |
---|---|---|---|
Planning | 計劃 | 60 | 90 |
Estimate | 估計這個任務需要多少時間,並規劃大致工作步驟 | 70 | 85 |
Development | 開發 | 2650 | 2850 |
Analysis | 需求分析 (包括學習新技術) | 60 | 60 |
Design Spec | 生成設計文件 | 50 | 60 |
Design Review | 設計複審 (和同事稽核設計文件) | 60 | 60 |
Coding Standard | 程式碼規範 (為目前的開發制定合適的規範) | 40 | 50 |
Design | 具體設計 | 80 | 85 |
Coding | 具體編碼 | 2000 | 2300 |
Test | 測試(自我測試,修改程式碼,提交修改) | 150 | 160 |
Reporting | 報告 | 200 | 210 |
Test Report | 測試報告 | 60 | 60 |
Size Measurement | 計算工作量 | 50 | 55 |
Postmortem & Process Improvement Plan | 事後總結 ,並提出過程改進計劃 | 120 | 150 |
- 需求分析:
- 平臺基礎功能:實驗二中的任務3
- D{0-1}KP例項資料集儲存到資料庫
- 平臺可動態嵌入任何一個有效的D{0-1}KP 例項求解演算法,並儲存演算法實驗日誌資料
- 人機互動介面要求為GUI介面
- 設計遺傳演算法求解D{0-1}KP
- 軟體設計說明
-
前端介面
- 前端的GUI使用的是layui的框架,前端介面的顯示是通過layui提供的模板,以及themeleaf、js等要素構成。
- 通過layui封裝的js,即可實現資料的非同步傳輸。
-
後臺功能
- 路由轉發:通過controller進行處理和轉發
- 資料庫的操作:通過從資料庫中獲取資料,構造json物件,返回到前端即可。
-
遺傳演算法
- 演算法介紹:是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜尋最優解的方法。該演算法通過數學的方式,利用計算機模擬運算,將問題的求解過程轉換成類似生物進化中的染色體基因的交叉、變異等過程。是一種近似演算法。
- 演算法流程:
- 關鍵步驟如下:
- 基因編碼:在這個過程中,嘗試對一些個體的基因做一個描述,構造這些基因的結構,有點像確定函式自變數的過程。
- 設計初始群體:在這裡需要造一個種群出來,這些種群有很多生物個體但基因不同。
- 適應度計算:這裡對那些不符合要求的後代進行剔除,不讓他們產生後代。否則他們產生的後代只會讓計算量更大而對逼近目標沒有增益。
- 產生下一代:有3種方法,即:直接選擇,基因重組,基因突變
而後回到第三步進行迴圈,適應度計算,產生下一代,這樣一代一代找下去,直到找到最優解為止。
-
前端介面
-
專案測試
-
GUI介面:
-
資料集有效資訊:
-
GUI介面:
-
實驗二任務3的移植:
- 資料集的檢視:
- 散點圖:
- 資料排序:
- 動態規劃演算法:
- 回溯演算法:
- 遺傳演算法:
- 資料集的檢視:
軟體實現及核心功能程式碼展示:
- 回溯演算法
private void dfs(int x){
back_count++;
if(back_count>INF){
res=-1;
return ;
}
if(x>=row) {
return ;
}
else {
if(weight[x+1][2]<=back_weight) {
back_weight-=weight[x+1][2];
back_value+=value[x+1][2];
if(res<back_value) {
res=back_value;
}
dfs(x+1);
back_weight+=weight[x+1][2];
back_value-=value[x+1][2];
}
dfs(x+1);
if(weight[x+1][0]<=back_weight) {
back_weight-=weight[x+1][0];
back_value+=value[x+1][0];
dfs(x+1);
if(res<back_value) {
res=back_value;
}
back_weight+=weight[x+1][0];
back_value-=value[x+1][0];
}
if(weight[x+1][1]<=back_weight) {
back_weight-=weight[x+1][1];
back_value+=value[x+1][1];
if(res<back_value) {
res=back_value;
}
dfs(x+1);
back_weight+=weight[x+1][1];
back_value-=value[x+1][1];
}
}
}
- 動態規劃演算法:
private Long knapSack(int[] weight, int[] profit, int C)
{
int n = profit.length/3;//profit一定是3的倍數
int[][] maxvalue = new int[n + 1][C + 1];//價值矩陣
long before=System.currentTimeMillis();
for (int i = 0; i < maxvalue.length; i++) {
maxvalue[i][0] = 0;
}
for (int i = 0; i < maxvalue[0].length; i++) {
maxvalue[0][i] = 0;
}
for (int i = 1; i < maxvalue.length; i++) {//不處理第一行
for (int j = 1; j <maxvalue[0].length; j++) {//不處理第一列
//處理每一個項集
int index=(i-1)*3;//計算當前的索引值,這裡以項集為單位進行計算
ArrayList<Integer> item=new ArrayList<>();
if (j<weight[index]&&j<weight[index+1]&&j<weight[index+2])
{
maxvalue[i][j]=maxvalue[i-1][j];
continue;
}
if(j>=weight[index])
item.add(Math.max(maxvalue[i-1][j],profit[index]+maxvalue[i-1][j-weight[index]]));
if(j>=weight[index+1])
item.add(Math.max(maxvalue[i-1][j],profit[index+1]+maxvalue[i-1][j-weight[index+1]]));
if(j>=weight[index+2])
item.add(Math.max(maxvalue[i-1][j],profit[index+2]+maxvalue[i-1][j-weight[index+2]]));
item.sort((Integer o1, Integer o2)->{
if (o1>o2) return -1;
else if (o1==o2) return 0;
else return 1;
});
maxvalue[i][j]=item.get(0);
}
}
long after=System.currentTimeMillis();
this.setOptimalSolution(maxvalue[n][C]);
return (after-before);
}
- 遺傳演算法:
// 初始化種群
private void initGroup() {
int k, i;
for (k = 0; k < scale; k++)// 種群數
{
// 01編碼
for (i = 0; i < LL; i++) {
oldPopulation[k][i] = random.nextInt(65535) % 2;
}
}
}
private best_one evaluate(int[] chromosome) {
// 010110
int vv = 0;
int bb = 0;
int str[]=new int[LL];
// 染色體,起始城市,城市1,城市2...城市n
for (int i = 0; i < LL; i++) {
if (chromosome[i] == 1) {
int temp=random.nextInt(65535) % 64;
if(temp<2) {
vv += v[i][temp];
bb += b[i][temp];
str[i] = temp + 1;
}
else{
vv += v[i][2];
bb += b[i][2];
str[i] = 3;
}
}
else {
str[i]=0;
}
}
if (bb > pb) {
// 超出揹包體積
best_one x =new best_one();
x.x=0;x.y=str;
return x;
} else {
best_one x =new best_one();
x.x=vv;x.y=str;
return x;
}
}
// 計算種群中各個個體的累積概率,前提是已經計算出各個個體的適應度fitness[max],作為賭輪選擇策略一部分,Pi[max]
private void countRate() {
int k;
double sumFitness = 0;// 適應度總和
int[] tempf = new int[scale];
for (k = 0; k < scale; k++) {
tempf[k] = fitness[k];
sumFitness += tempf[k];
}
Pi[0] = (float) (tempf[0] / sumFitness);
for (k = 1; k < scale; k++) {
Pi[k] = (float) (tempf[k] / sumFitness + Pi[k - 1]);
}
}
// 挑選某代種群中適應度最高的個體,直接複製到子代中
// 前提是已經計算出各個個體的適應度Fitness[max]
private void selectBestGh() {
int k, i, maxid;
int maxevaluation;
int max_str[] = null;
maxid = 0;
maxevaluation = fitness[0];
for (k = 1; k < scale; k++) {
if (maxevaluation < fitness[k]) {
maxevaluation = fitness[k];
max_str=fitness_str[k];
maxid = k;
}
}
if (bestLength < maxevaluation) {
bestLength = maxevaluation;
best_str=max_str;
bestT = t;// 最好的染色體出現的代數;
for (i = 0; i < LL; i++) {
bestTour[i] = oldPopulation[maxid][i];
}
}
// 複製染色體,k表示新染色體在種群中的位置,kk表示舊的染色體在種群中的位置
copyGh(0, maxid);// 將當代種群中適應度最高的染色體k複製到新種群中,排在第一位0
}
// 賭輪選擇策略挑選
private void select() {
int k, i, selectId;
float ran1;
for (k = 1; k < scale; k++) {
ran1 = (float) (random.nextInt(65535) % 1000 / 1000.0);
// System.out.println("概率"+ran1);
// 產生方式
for (i = 0; i < scale; i++) {
if (ran1 <= Pi[i]) {
break;
}
}
selectId = i;
copyGh(k, selectId);
}
}
private void evolution() {
int k;
// 挑選某代種群中適應度最高的個體
selectBestGh();
// 賭輪選擇策略挑選scale-1個下一代個體
select();
float r;
// 交叉方法
for (k = 0; k < scale; k = k + 2) {
r = random.nextFloat();// /產生概率
// System.out.println("交叉率..." + r);
if (r < Pc) {
// System.out.println(k + "與" + k + 1 + "進行交叉...");
OXCross(k, k + 1);// 進行交叉
} else {
r = random.nextFloat();// /產生概率
// System.out.println("變異率1..." + r);
// 變異
if (r < Pm) {
// System.out.println(k + "變異...");
OnCVariation(k);
}
r = random.nextFloat();// /產生概率
// System.out.println("變異率2..." + r);
// 變異
if (r < Pm) {
// System.out.println(k + 1 + "變異...");
OnCVariation(k + 1);
}
}
}
}
// 兩點交叉運算元
private void OXCross(int k1, int k2) {
int i, j, flag;
int ran1, ran2, temp = 0;
ran1 = random.nextInt(65535) % LL;
ran2 = random.nextInt(65535) % LL;
while (ran1 == ran2) {
ran2 = random.nextInt(65535) % LL;
}
if (ran1 > ran2)// 確保ran1<ran2
{
temp = ran1;
ran1 = ran2;
ran2 = temp;
}
flag = ran2 - ran1 + 1;// 個數
for (i = 0, j = ran1; i < flag; i++, j++) {
temp = newPopulation[k1][j];
newPopulation[k1][j] = newPopulation[k2][j];
newPopulation[k2][j] = temp;
}
}
// 多次對換變異運算元
private void OnCVariation(int k) {
int ran1, ran2, temp;
int count;// 對換次數
count = random.nextInt(65535) % LL;
for (int i = 0; i < count; i++) {
ran1 = random.nextInt(65535) % LL;
ran2 = random.nextInt(65535) % LL;
while (ran1 == ran2) {
ran2 = random.nextInt(65535) % LL;
}
temp = newPopulation[k][ran1];
newPopulation[k][ran1] = newPopulation[k][ran2];
newPopulation[k][ran2] = temp;
}
}
private void solve() {
int i;
int k;
// 初始化種群
initGroup();
// 計算初始化種群適應度,Fitness[max]
for (k = 0; k < scale; k++) {
best_one temp= evaluate(oldPopulation[k]);
fitness[k]=temp.x;
fitness_str[k]=temp.y;
}
// 計算初始化種群中各個個體的累積概率,Pi[max]
countRate();
for (t = 0; t < MAX_GEN; t++) {
evolution();
// 將新種群newGroup複製到舊種群oldGroup中,準備下一代進化
for (k = 0; k < scale; k++) {
for (i = 0; i < LL; i++) {
oldPopulation[k][i] = newPopulation[k][i];
}
}
// 計算種群適應度
for (k = 0; k < scale; k++) {
best_one temp= evaluate(oldPopulation[k]);
fitness[k]=temp.x;
fitness_str[k]=temp.y;
}
// 計算種群中各個個體的累積概率
countRate();
}
}
- 結對過程
-
總結:
在組員積極交流且按照極限程式設計的方式進行專案開發是可以帶來1+1>2的效果。結對程式設計要特別注意程式碼的規範問題,結對程式設計時要多保持交流,積極反饋當前遇到的問題與解決措施。在結對初期,由於對每個人的定位不夠清楚,導致合作效率並沒有想象的快,在多次溝通後也是解決了這一問題。而且結對的人都要努力完成自己的目標,如果兩個人在一起工作時,其中一個人想偷懶去幹別的,那麼就會拖延工作進度。畢竟要把每個人所負責的部分結合在一起並不是一件容易的事,只有兩人互相監督工作,才可以更高效的完成專案。同時兩個人互相監督工作,還可以增強程式碼和產品質量,並有效的減少BUG。這為之後軟體的易維護性奠定了基礎。在程式設計中,相互討論,可以更快更有效地解決問題,互相請教對方,可以得到能力上的互補。