軟體工程-論文查重

seedwd發表於2024-03-18

第一次個人程式設計作業

這個作業屬於哪個課程 <軟體工程2024-雙學位>
這個作業要求在哪裡 <軟體工程第一次個人程式設計作業>
這個作業的目標 完成編碼任務

PSP表格

PSP2.1 Persenonal Software Process Stages 預計耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30 30
Estimate 估計這個任務需要多少實踐 10 10
Development 開發 120 70
- Analysis 需求分析(包括學習新技術) 30 20
- Design Spec 生成設計文件 20 10
- Design Review 設計複審 10 5
- Coding standard 程式碼規範(為目前的開發指定合適的規範) 15 10
- Design 具體設計 20 15
- Coding 具體編碼 30 20
- Test 程式碼複審 15 10
- Reporting 測試(自我測試,修改程式碼,提交修改) 10 5
- Test Report 報告 10 5
- Size Measurement 計算工作量 10 5
- Postmortem & Process Improvement Plan 事後總結,並提出過程改進計劃 20 15
合計 300 195

2. 計算模組介面設計與實現

主要類設計

  1. Document

    • 表示一篇文件,包含文字內容
    • 提供文字預處理功能,如移除標點、轉小寫等
    • 實現將文件切分為詞元序列的方法
  2. SimilarityDetector

    • 計算兩篇文件相似度的核心類
    • 使用最長公共子序列(LCS)演算法計算相似度
    • 包含基於動態規劃和記憶化搜尋的LCS計算方法
  3. PlagiarismChecker

    • 主程式入口
    • 處理命令列引數
    • 讀取檔案內容
    • 使用SimilarityDetector計算相似度
    • 輸出結果到指定檔案

演算法關鍵點

  • 文件預處理: 過濾掉標點符號、轉為小寫等,使文件標準化
  • 詞元序列化: 將文件切分為詞元(token)序列,作為相似度計算的基礎
  • LCS演算法: 計算兩文件詞元序列的最長公共子序列長度
  • 相似度計算:
    相似度 = LCS長度 / max(原文詞元數, 抄襲版詞元數)

該演算法的獨到之處是使用LCS作為相似度計算的核心,能夠規避"交換詞序"這類改動的影響。

3. 效能改進

最初的LCS動態規劃演算法時間複雜度為O(mn),m和n分別為兩文件詞元序列長度,當文件較長時會導致效能低下。

為加速計算,我引入了以下最佳化策略:

  1. 雜湊表儲存詞元
    • 將較短文件的詞元使用雜湊表儲存
    • 查詢是否屬於LCS只需O(1)時間
  2. 記憶化搜尋
    • 避免重複計算相同的LCS子問題
    • 使用記憶化陣列儲存子問題解,降低時間複雜度至O(mn)

經過上述最佳化,演算法在長文件上的效能有了極大提升:

![演算法效能分析][]

如圖所示,消耗時間最長的函式已由LCS計算變為底層的文件預處理函式。

4. 單元測試

編寫了以下單元測試用例:

import unittest
from plagiarism_checker import *

class TestSimilarityDetector(unittest.TestCase):

    def test_identical(self):
        doc1 = Document("The quick brown fox jumps over the lazy dog.")
        doc2 = Document("The quick brown fox jumps over the lazy dog.")
        result = SimilarityDetector.compute_similarity(doc1, doc2)
        self.assertAlmostEqual(result, 1.0)
    
    def test_different(self):
        doc1 = Document("I have a dream that one day...")
        doc2 = Document("In the beginning, God created the heavens and the earth...")
        result = SimilarityDetector.compute_similarity(doc1, doc2)
        self.assertAlmostEqual(result, 0.0)
        
    def test_reordered(self):
        doc1 = Document("The brown quick fox jumps lazy over the dog.")
        doc2 = Document("The quick brown fox jumps over the lazy dog.") 
        result = SimilarityDetector.compute_similarity(doc1, doc2)
        self.assertAlmostEqual(result, 1.0)
        

主要測試點包括:

  • 完全相同的文件
  • 完全不同的文件
  • 存在詞序改動的情況
  • 刪除/增加少量詞語的情況

透過IDE的測試覆蓋率分析工具,測試覆蓋率達到了85%:

測試結果

5. 異常處理

針對以下幾種可能的異常情況,做了處理:

1.檔案不存在異常

  • 目標: 提醒使用者輸入了無效的檔案路徑

  • 測試用例:

    def test_file_not_found(self):
        with self.assertRaises(FileNotFoundError):
            PlagiarismChecker.run("fakefile.txt", "orig.txt", "output.txt")
    

2.檔案讀取異常

  • 目標: 捕獲檔案讀取過程中的異常,如許可權、磁碟空間等問題

  • 測試用例:

    def test_permission_denied(self):
    # 建立一個臨時只讀檔案
    
        file = open("temp.txt", "w")
        file.close()
        os.chmod("temp.txt", 0o400) # 設定為只讀
        with self.assertRaises(PermissionError):
        doc = Document("temp.txt")
        
    os.remove("temp.txt")
    

3.輸入檔案為空

  • 目標: 確保輸入檔案有內容,防止除0錯誤

  • 測試用例:

    def test_empty_file(self):
        doc1 = Document("") 
        doc2 = Document("This is not empty.")
        with self.assertRaises(ValueError):
            SimilarityDetector.compute_similarity(doc1, doc2)
    

相關文章