這個作業屬於哪個課程 | 班級鏈接 |
---|---|
這個作業要求在哪里 | 作業鏈接 |
這個作業的目標 | 論文查重算法設計+學習PSP表格+單元測試+JProfiler性能分析+Git管理 |
1 代碼鏈接(Java)
1.1 Github鏈接
1.2 可運行的jar包已經發布至倉庫的release包內
2 計算模塊接口的設計與實現過程
2.1 主要的類
-
IoProcess類:進行文本輸入輸出的操作,文本和字符串的相互轉換
-
MyApplication類:主程序
-
MyApplicationTest類:對程序進行單元測試
-
MySimHash類(關鍵類):對字符串進行分詞、獲取哈希值、加權、合並、降維操作,並計算兩字符串的海明距離,計算相似度
2.2 關鍵類的關鍵函數
2.2.1 getSimHash()
-
分詞:調用HanLp包,對全部字符串進行分詞
-
獲取哈希值:調用hash(),獲取每個詞的哈希值,256bits
-
加權和合並:根據詞語的索引進行加權,例如 0~9 的權重為10/-10, 10~19的權重為9/-9,依次遞減,並將相同索引的hash值通過相加進行合並
-
降維:對每個索引的哈希值大於0的置為1,小於0的置為0
2.2.2 例子
-
分詞,我們假設權重分為5個級別(1~5)。比如:“今天是星期天,天氣晴,今天晚上我要去看電影 ” ==> 分詞后為 “ 今天/ 是/星期/天/天氣/晴/晚上/我/要去/看電影“,分詞。
-
hash,通過hash算法把每個詞變成hash值,比如“今天”通過hash算法計算為 110101(算法中是設置了196bit的值),“是”通過gethash算法計算為 101011。這樣我們的字符串就變成了一串串數字。
-
加權,通過 2步驟的hash生成結果,需要按照單詞的權重(單詞權重按照先后順序划分,上面有說過)形成加權數字串,比如“今天”的hash值為“110101”,通過加權計算為“10 10 -10 10 -10 10”;“是”的hash值為“101011”,通過加權計算為 “ 9 -9 9 -9 9 9”。
-
合並,把上面各個單詞算出來的序列值累加,變成只有一個序列串。“10+9 10-9 -10+9 10-9 -10+9 10+9”,這里為了方便,沒有將所有的都寫出來,實際上是將所有的進行相加。
-
降維,把4步算出來的 “19 1 -1 1 -1 19” 變成 0 1 串,形成我們最終的simhash簽名。 如果每一位大於0 記為 1,小於0 記為 0。最后算出結果為:“1 1 0 1 0 1”。
2.2.3 getSembalance()
對比兩個哈希值相同索引,如果不同,海明距離加1,最后計算相似度,保留兩位小數
2.3 算法的關鍵以及獨到之處
2.3.1 介紹
運用simHash,而不是直接運用Hash,simhash一種局部敏感的hash值,本文主要目的是為比較兩文本的相似度,如果使用傳統的hash,無法實現目的。
2.3.2 例子
“你媽媽喊你回家吃飯哦,回家羅回家羅” 和 “你媽媽叫你回家吃飯啦,回家羅回家羅”。
通過simhash計算結果為:
-
1000010010101101111111100000101011010001001111100001001011001011
-
1000010010101101011111100000101011010001001111100001101010001011
通過傳統hash計算為:
-
0001000001100110100111011011110
-
1010010001111111110010110011101
可見,simhash變化的只有很小一部分,而hash值發生了很大改變,因此不能用傳統的hash
2.4 分包截圖
2.5 實際命令行運行效果
3 計算模塊接口部分的性能改進
執行單元測試,對各種情況使用JProfiler對性能進行監控
3.1 類的占用情況
3.2 CPU Load
3.3堆內存情況
3.4 耗時操作
可見,耗時最多的是hanlp分詞操作,因此性能瓶頸在於Hanlp分詞操作函數,所以我並沒有對其他地方進行改動來提升程序的性能
4 計算模塊部分單元測試展示
4.1 采用白盒測試,對原文件改進,空文件、添加、刪除、錯別字,打亂等測試
單元測試代碼如下
/**
*Test 1:test the empty txtFile
*/
@Test
public void testForEmpty(){
try {
MyApplication.process("test/orig.txt", "test/empty.txt", "test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
}
/**
* Test 3:Test 20% text addition: orig_0.8_add.txt
*/
@Test
public void testForAdd(){
try { MyApplication.process("test/orig.txt","test/orig_0.8_add.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
}
/**
* Test 4:Test 20% text deletion: orig_0.8_del.txt
*/
@Test
public void testForDel(){
try {
MyApplication.process("test/orig.txt","test/orig_0.8_del.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Test 5:Test 20% out-of-order text: orig_0.8_dis_1.txt
*/
@Test
public void testForDis1(){
try {
MyApplication.process("test/orig.txt","test/orig_0.8_dis_1.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Test 6:Test 20% out-of-order text: orig_0.8_dis_10.txt
*/
@Test
public void testForDis10(){
try { MyApplication.process("test/orig.txt","test/orig_0.8_dis_10.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Test 7:Test 20% out-of-order text: orig_0.8_dis_15.txt
*/
@Test
public void testForDis15(){
try {
MyApplication.process("test/orig.txt","test/orig_0.8_dis_15.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Test 8:Test the same text: orig.txt
*/
@Test
public void testForSame(){
try {
MyApplication.process("test/orig.txt","test/orig.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
4.2 測試結果
4.3 代碼覆蓋率
5 計算模塊部分異常處理說明
5.1 測試文件輸入參數出錯的情況
代碼如下(只展示部分代碼):
/**
*Test 2:The case where the entered comparison text path parameter is an incorrect parameter
*/
public void testForWrongOriginArgument(){
try {
MyApplication.process("test/123.txt","test/orig_0.8_add.txt","test/ansAll.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
測試結果:
5.2 測試文件輸出參數出錯的情況
代碼如下(只展示部分代碼):
/**
*Test14: Test if the output file path parameter is an error parameter
*/
@Test
public void testForWrongOutputArgument(){
try {
MyApplication.process("test/orig.txt","test/orig.txt","test/testAWrongArgumentResult.txt");
}
catch (Exception e) {
e.printStackTrace();
}
}
測試結果:
5.3 測試文件為空的情況
代碼如下(只展示部分代碼):
/**
* Test 1:test the empty txtFile
*/
@Test
public void testForEmpty(){
try {
MyApplication.process("test/orig.txt", "test/empty.txt", "test/ansAll.txt");
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
}
測試結果:
6 PSP表格
PSP2.1 | Personal Software Process Stage | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 20 | 30 |
Estimate | 估計這個任務需要多少時間 | 60 | 60 |
Development | 開發 | 600 | 600 |
Analysis | 需求分析 (包括學習新技術) | 240 | 300 |
Design Spec | 生成設計文檔 | 70 | 60 |
Design Review | 設計復審 | 30 | 30 |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 30 | 20 |
Design | 具體設計 | 60 | 70 |
Coding | 具體編碼 | 200 | 240 |
Code Review | 代碼復審 | 40 | 60 |
Test | 測試(自我測試,修改代碼,提交修改) | 240 | 300 |
Reporting | 報告 | 160 | 180 |
Test Repor | 測試報告 | 60 | 60 |
Size Measurement | 計算工作量 | 40 | 40 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 60 | 60 |
合計 | 1910 | 2110 | |
7 代碼質量檢查Code Quantity Analysis
代碼質量檢查,用的是codacy,主要問題出現在類名和方法名的命名上,復習了正確的命名方式
對於類名,大駝峰式,符合正則表達式:[A-Z]a-zA-Z0-9
對於方法名,小駝峰式,符合正則表達式:[a-z]a-zA-Z0-9
8 總結
- 因為本人沒有完全寫過一個類似於這種的開發程序,所以耗時比較久,主要耗時在於Git和Github的學習上,如何將本地文件上傳到倉庫上;
- 耗時比較多的地方還有對於jar包的打包上,因為沒有將Maven等依賴包一起打包進去,所以導致出現錯誤,最后詢問了朋友才解決了問題;
- 以及之前沒有接觸過的codacy、Jprofiler和單元測試,所以學習新的工具也需要一定的時間