軟件工程 | https://edu.cnblogs.com/campus/gdgy/informationsecurity1812 |
---|---|
做業要求 | https://edu.cnblogs.com/campus/gdgy/informationsecurity1812/homework/11155 |
做業目標 | 論文查重算法+單元測試+JProfiler+PSP表格+Git管理 |
GitHub連接java
可運行的Jar包已發佈至倉庫的release包內android
類、函數之間的關係經過IDEA自帶生成的UML圖直觀地呈現git
org.odm 包內的UML圖github
utils包內的UML圖算法
實際命令行運行效果框架
算法的關鍵函數
基於一個概念——餘弦距離,也稱爲餘弦類似度,是用向量空間中兩個向量夾角的餘弦值做爲衡量兩個個體間差別的大小的度量。餘弦值越接近1,就代表夾角越接近0度,也就是兩個向量越類似,這就叫"餘弦類似性"。工具
獨到之處性能
複雜文本狀況下,速度能夠維持在2s內,簡單狀況下如文本高度相同,速度能夠達到20ms,同時識別準確率也很不錯。計算速度和準確度達到了相對均衡。單元測試
用餘弦類似度算法計算文本的類似性。
爲了簡單起見,先從句子着手。
句子A:這頂帽子尺寸大了。那頂尺寸合適。
句子B:這頂帽子尺寸不小,那頂更合適。
基本計算的思路是:若是這兩句話的用詞越類似,它們的內容就應該越類似。
所以,能夠從詞頻入手,計算它們的類似程度。
第一步,分詞
句子A:這頂/帽子/尺寸/大了。那頂/尺寸/合適。
句子B:這頂/帽子/尺寸/不/小,那頂/更/合適。
第二步,計算詞頻
句子A:這頂(1),帽子(1),尺寸(2),大了(1),那頂(1),合適(1),不(0),小(0),更(0)
句子B:這頂(1),帽子(1),尺寸(1),大了(0),那頂(1),合適(1),不(1),小(1),更(1)
第三步,寫出詞頻向量
句子A:(1,1,2,1,1,1,0,0,0)
句子B:(1,1,1,0,1,1,1,1,1)
第四步:運用上面的公式:計算以下:
計算結果中夾角的餘弦值爲0.81,很是接近於1。
因此,上面的句子A和句子B是基本類似的
展現單元測試代碼(12種狀況)
public class MainApplicationTest { @BeforeClass public static void beforeTest(){ System.out.println("測試即將開始"); } @AfterClass public static void afterTest(){ System.out.println("測試結束"); } /** * 測試 文本爲空文本的狀況 */ @Test public void testForEmpty(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/empty.txt","src/test/result/testEmptyResult.txt"); } /** * 測試 輸入的對比文本路徑參數爲錯誤參數的狀況 */ @Test public void testForWrongOriginArgument(){ MainApplication.process("src/test/testcase/123.txt","src/test/testcase/orig_0.8_add.txt","src/test/result/testAddResult.txt"); } /** * 測試 輸出文件路徑參數爲錯誤參數的狀況 */ @Test public void testForWrongOutputArgument(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig.txt","src/test/result/testAddResult.word"); } /** * 測試20%文本添加狀況:orig_0.8_add.txt */ @Test public void testForAdd(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_add.txt","src/test/result/testAddResult.txt"); } /** * 測試20%文本刪除狀況:orig_0.8_del.txt */ @Test public void testForDel(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_del.txt","src/test/result/testDelResult.txt"); } /** * 測試20%文本亂序狀況:orig_0.8_dis_1.txt */ @Test public void testForDis1(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_dis_1.txt","src/test/result/testDis1Result.txt"); } /** * 測試20%文本亂序狀況:orig_0.8_dis_3.txt */ @Test public void testForDis3(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_dis_3.txt","src/test/result/testDis3Result.txt"); } /** * 測試20%文本亂序狀況:orig_0.8_dis_7.txt */ @Test public void testForDis7(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_dis_7.txt","src/test/result/testDis7Result.txt"); } /** * 測試20%文本亂序狀況:orig_0.8_dis_10.txt */ @Test public void testForDis10(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_dis_10.txt","src/test/result/testDis10Result.txt"); } /** * 測試20%文本亂序狀況:orig_0.8_dis_15.txt */ @Test public void testForDis15(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_dis_15.txt","src/test/result/testDis15Result.txt"); } /** * 測試20%文本格式錯亂狀況:orig_0.8_mix.txt */ @Test public void testForMix(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_mix.txt","src/test/result/testMixResult.txt"); } /** * 測試20%文本錯別字狀況:orig_0.8_rep.txt */ @Test public void testForRep(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_0.8_rep.txt","src/test/result/testRepResult.txt"); } /** * 測試相同文本:orig.txt */ @Test public void testForSame(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig.txt","src/test/result/testSameResult.txt"); } /** * 測試文本的子集文本:orig_sub.txt */ @Test public void testForSub(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig_sub.txt","src/test/result/testSubResult.txt"); } }
說明測試的方法,構造測試數據的思路
測試結果
測試覆蓋率截圖
IOException以及FileNotFoundException,異常的場景是文件的寫入和讀取以及文件不存在仍要操做,可能會致使這些異常,因此要提早規避。
以下:
對應的測試
/** * 測試 文本內容爲空文本的狀況 */ @Test public void testForEmpty(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/empty.txt","src/test/result/testEmptyResult.txt"); } /** * 測試 輸入的對比文本路徑參數爲錯誤參數的狀況 */ @Test public void testForWrongOriginArgument(){ MainApplication.process("src/test/testcase/123.txt","src/test/testcase/orig_0.8_add.txt","src/test/result/testAddResult.txt"); } /** * 測試 輸出文件路徑參數爲錯誤參數的狀況 */ @Test public void testForWrongOutputArgument(){ MainApplication.process("src/test/testcase/orig.txt","src/test/testcase/orig.txt","src/test/result/testAddResult.word"); }
測試結果
執行單元測試,對各類狀況進行測試的同時使用 JProfiler對性能進行監控
類的內存消耗
CPU Load(運行時間:1.1 s,知足要求)
由圖能夠看出,改進前的程序中時間平均耗時最大的方法——Hanlp的分詞操做
摸索了大概20分鐘,最後發現因爲最耗時的地方是在於分詞操做的函數,而若是一味提升速度就會損失精度,因此沒法從Hanlp的分詞函數動刀。故只好從其餘耗時地方(對象建立等)入手,例子以下:
PSP 各個階段 | 本身預估的時間(分鐘) | 實際的記錄(分鐘) |
---|---|---|
計劃: 明確需求和其餘因素,估計如下的各個任務須要多少時間 | 30 | 45 |
開發 (包括下面 8 項子任務) | (如下都填預估值) | 218 |
· 需求分析 (包括學習新技術、新工具的時間) | 20 | 30 |
· 生成設計文檔 (總體框架的設計,各模塊的接口,用時序圖,快速原型等方法) | 15 | 5 |
· 設計複審 (和同事審覈設計文檔,或者本身複審) | 15 | 20 |
· 代碼規範 (爲目前的開發制定或選擇合適的規範) | 5 | 3 |
· 具體設計(用僞代碼,流程圖等方法來設計具體模塊) | 20 | 30 |
· 具體編碼 | 60 | 75 |
· 代碼複審 | 15 | 20 |
· 測試(自我測試,修改代碼,提交修改) | 30 | 35 |
報告 | 75 | 95 |
測試報告(發現了多少bug,修復了多少) | 15 | 20 |
計算工做量 (多少行代碼,多少次簽入,多少測試用例,其餘工做量) | 10 | 15 |
過後總結, 並提出改進計劃 (包括寫文檔、博客的時間) | 50 | 60 |
總共花費的時間 (分鐘) | 290 | 358 |
看來仍是對本身太自信了~將來儘可能實際追上計劃吧