第一次个人编程作业

第一次个人编程作业

这个作业属于那个课程 计科23级12班
这个作业的要求在哪里 个人项目
这个作业的目标 完成项目编码,记录程序开发花费时间PSP表,并对代码项分析和单元测试

1.Github链接https://github.com/Yzttt0425/yanzhantong.github/tree/main/3223004388

2. PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟)
Planning 计划 120
· Estimate · 估计这个任务需要多少时间 120
Development 开发 655
· Analysis · 需求分析 (包括学习新技术) 120
· Design Spec · 生成设计文档 30
· Design Review · 设计复审 15
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10
· Design · 具体设计 60
· Coding · 具体编码 240
· Code Review · 代码复审 30
· Test · 测试(自我测试,修改代码,提交修改) 150
Reporting 报告 110
· Test Repor · 测试报告 60
· Size Measurement · 计算工作量 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30
· 合计 985

3.模块接口的设计与实现过程
3.1项目结构
text/ # 项目根目录
├── src/ # 源代码目录
│ ├── io/ # IO操作模块(文件读写)
│ │ └── FileHandler.java # 文件读取/写入工具类
│ ├── text/ # 文本处理与计算核心模块
│ │ ├── TextProcessor.java # 文本预处理与分词工具类
│ │ ├── SimilarityCalculator.java # 相似度计算核心类
│ │ └── Main.java # 程序入口(命令行交互)
├── out/ # 编译输出目录(class文件)
3.2 功能介绍

类名 功能 关键方法
FileHandler 负责文件 IO 操作,包括文件读取、写入及基础校验(存在性、权限等) readFile()(读取文件内容)、writeFile()(写入内容到文件)
TextProcessor 负责文本预处理与分词,为相似度计算提供标准化输入 preprocess()(文本清洗)、segment()(混合 n-gram 分词)、buildWordFrequency()(词频统计)
SimilarityCalculator 实现核心的文本相似度计算算法 calculateSimilarity()(基于加权余弦相似度的计算)
Main 作为程序入口,处理命令行参数、协调各模块流程(读文件→计算→写结果) main()(流程控制)

3.3 类函数关系图
image

  • 关键函数:SimilarityCalculator.calculateSimilarity()
    3.4算法关键
    (1)文本预处理
    过滤无意义特殊符号,剔除含空格无效过度虚词。
    (2)混合N-gram分词
    2-gram捕捉局部细节,3-gram保留正序列结构信息
    (3)余弦相似度
    image
    赋予gram权重
    3.5独到之处
    (1)混合n-gram分词
    解决传统单一分词的局限性
    (2)加权余弦相似度计算
    提高准确性
    (3)边界与异常处理
    空文本处理和防零防护
    4.模块接口部分的性能改进
    (1)用混合n-gram方法代替单一n-gram方法,能更全面地保留文本结构信息,提升相似内容的识别能力。
    (2)改用哈希表进行词频统计,将时间复杂度从O(n*n)优化至O(n)。
    (3)加权余弦相似度代替单一余弦相似度,对 “出现次数≥2” 的 gram 赋予 1.2 倍权重,便面低频次、噪声gram干扰。
    屏幕截图 2025-09-22 231103

屏幕截图 2025-09-22 231048
改进后,长文本的相似度计算耗时从300ms+降至150ms,短文本从50ms降至20ms。
可看出,calculateSimilarity()是消耗最大的函数。
5.模块部分单元测试
5.1部分单元测试代码
用JUnit4测试

点击查看代码
public class SimilarityCalculatorTest {

    // 测试完全相同的文本(相似度应为 1.00)
    @Test
    public void testCalculateSimilarity_IdenticalText() {
        String text1 = "这是一篇原创论文,讨论人工智能的应用。";
        String text2 = "这是一篇原创论文,讨论人工智能的应用。";
        double similarity = SimilarityCalculator.calculateSimilarity(text1, text2);
        assertEquals(1.00, similarity, 0.01); // 允许0.01的误差
    }

    // 测试完全不同的文本(相似度应为 0.00)
    @Test
    public void testCalculateSimilarity_CompletelyDifferent() {
        String text1 = "人工智能";
        String text2 = "量子计算";
        double similarity = SimilarityCalculator.calculateSimilarity(text1, text2);
        assertEquals(0.00, similarity, 0.01);
    }

    // 测试部分相似的文本(相似度应为 0.5~0.8 之间,具体值需计算)
    @Test
    public void testCalculateSimilarity_PartiallySimilar() {
        String text1 = "这是一篇原创论文,讨论人工智能在医疗领域的应用。";
        String text2 = "这是一篇抄袭论文,讨论人工智能在医疗领域的应用。";
        double similarity = SimilarityCalculator.calculateSimilarity(text1, text2);
        // 核心短语(人工智能、医疗领域、应用)相同,相似度应较高
        assertTrue(similarity > 0.7 && similarity < 0.9);
    }

    // 测试空文本场景
    @Test
    public void testCalculateSimilarity_EmptyText() {
        // 两个空文本(相似度 1.00)
        double sim1 = SimilarityCalculator.calculateSimilarity("", "");
        assertEquals(1.00, sim1, 0.01);

        // 一个空文本,一个非空文本(相似度 0.00)
        double sim2 = SimilarityCalculator.calculateSimilarity("测试", "");
        assertEquals(0.00, sim2, 0.01);

        double sim3 = SimilarityCalculator.calculateSimilarity(null, "测试");
        assertEquals(0.00, sim3, 0.01);
    }

    // 测试混合语言(中英文+数字)的相似度
    @Test
    public void testCalculateSimilarity_MixedLanguage() {
        String text1 = "AI(人工智能)的发展始于1956年的Dartmouth会议。";
        String text2 = "人工智能(AI)的发展始于1956年的Dartmouth会议。";
        double similarity = SimilarityCalculator.calculateSimilarity(text1, text2);
        assertEquals(1.00, similarity, 0.01); // 仅语序微调,核心gram相同
    }

    // 测试高频gram的权重放大效果
    @Test
    public void testCalculateSimilarity_HighFrequencyGram() {
        // text1 和 text2 中 "人工智能" 均出现3次(高频gram,权重1.2)
        String text1 = "人工智能 人工智能 人工智能 机器学习";
        String text2 = "人工智能 人工智能 人工智能 深度学习";
        double similarity = SimilarityCalculator.calculateSimilarity(text1, text2);
        // 高频gram权重放大后,相似度应高于普通情况
        assertTrue(similarity > 0.75);
    }
    private String generateLongText(String baseParagraph, int repeatCount) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < repeatCount; i++) {
            sb.append(baseParagraph);
            if ((i + 1) % 10 == 0) sb.append("\n"); // 每10段加换行(模拟段落结构)
        }
        return sb.toString();
    }

    /**
     * 测试1万字完全相同的长文本
     * 预期:相似度1.00,程序不崩溃
     */
    @Test
    public void testCalculateSimilarity_LongIdenticalText() {
        // 基础段落(约50字),重复200次 → 约1万字
        String base = "深度学习框架是构建神经网络的工具,主流框架包括TensorFlow、PyTorch和Keras,简化了模型开发流程。";
        String text1 = generateLongText(base, 200);
        String text2 = generateLongText(base, 200);

        double sim = SimilarityCalculator.calculateSimilarity(text1, text2);
        assertEquals("长文本完全相同相似度错误", 1.00, sim, 0.2); 
       
    }

    /**
     * 测试90%相似的长文本(10%内容修改)
     * 预期:相似度0.85~0.95
     */
    @Test
    public void testCalculateSimilarity_LongHighSimilar() {
        // 基础段落(约50字),生成1万字原创文本
        String originalBase = "数据挖掘是从大量数据中提取价值信息的过程,结合统计学、AI和数据库技术,用于商业决策。";
        String original = generateLongText(originalBase, 200);

        // 生成90%相似的抄袭文本(每10段修改1段)
        String modifiedBase = "数据挖掘是从海量数据中提取有用信息的技术,融合统计学、人工智能和数据库方法,服务于商业分析。";
        StringBuilder plagiarized = new StringBuilder();
        for (int i = 0; i < 200; i++) {
            if (i % 10 == 0) {
                plagiarized.append(modifiedBase);
            } else {
                plagiarized.append(originalBase);
            }
            if ((i + 1) % 10 == 0) plagiarized.append("\n");
        }

        double sim = SimilarityCalculator.calculateSimilarity(original, plagiarized.toString());
        
        assertTrue("长文本高相似度计算异常", sim > 0.8 && sim < 0.95);
    }

   
    private String generateLong(String base, int repeat) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < repeat; i++) {
            sb.append(base);
        }
        return sb.toString();
    }

    /** 部分相似的长文本(50%内容相同)→ 相似度应在 0.4~0.6 之间 */
    @Test
    public void testPartialSimilarLongText() {
        String sameBase = "人工智能的发展分为弱AI、强AI和超AI三个阶段。";
        String diffBase1 = "弱AI专注特定任务,如语音助手、图像识别。";
        String diffBase2 = "强AI追求通用智能,能像人类一样推理和学习。";

        StringBuilder text1 = new StringBuilder();
        StringBuilder text2 = new StringBuilder();
        for (int i = 0; i < 50; i++) {
            text1.append(sameBase).append(diffBase1);
            text2.append(sameBase).append(diffBase2);
        }

        double sim = SimilarityCalculator.calculateSimilarity(text1.toString(), text2.toString());
        assertTrue("部分相似长文本相似度异常", sim > 0.4 && sim < 0.6);
    }

}

5.2测试说明

测试函数 测试目标 构造思路
testCalculateSimilarity_IdenticalText 验证完全相同的短文本相似度计算准确性,确保程序能精准识别内容完全一致的文本 输入两段完全相同的中文短文本,验证计算出的相似度接近 1.00
testCalculateSimilarity_CompletelyDifferent 验证完全不同的短文本相似度计算准确性,确保程序能识别无内容重叠的文本 输入两段主题无关的短文本,验证计算出的相似度接近 0.00(误差允许 0.01)。
testCalculateSimilarity_PartiallySimilar 验证部分相似的短文本(核心短语相同、非核心部分不同)相似度计算合理性 输入两段含相同核心短语但非核心内容不同的文本,验证相似度在 0.7~0.9 之间。
testCalculateSimilarity_EmptyText 验证空文本场景(空字符串、null、空与非空组合)的边界处理逻辑,确保程序稳定且结果正确 设计三种输入:空文本 vs 空文本、空文本 vs 非空文本、null vs 非空文本,分别通过验证结果:空 vs 空为 1.00,含空 /null 为 0.00(误差允许 0.01)。
testCalculateSimilarity_MixedLanguage 验证混合语言文本(中 / 英 / 数字混合)在核心内容相同、语序微调时的相似度计算准确性 输入两段核心内容一致但中英术语顺序不同的文本
testCalculateSimilarity_HighFrequencyGram 验证高频重复片段(gram)的权重放大逻辑是否生效,确保重复内容对相似度的影响被放大 输入两段含多次重复相同短语(如 “人工智能” 出现 3 次)但后续内容不同的文本
testCalculateSimilarity_LongIdenticalText 验证1 万字完全相同长文本的计算稳定性和准确性,确保程序处理大文本不崩溃且结果正确 通过重复基础段落生成两段完全相同的 1 万字长文本
testPartialSimilarLongText 验证50% 相似长文本的计算合理性,确保程序能区分部分重叠的长文本 生成两段各含 50% 相同内容(sameBase)和 50% 不同内容(diffBase1/diffBase2)的长文本
testCalculateSimilarity_LongHighSimilar 验证90% 相似长文本的计算合理性,确保程序能识别大部分内容重叠的长文本 生成原创长文本和 90% 内容相同(仅 10% 轻微修改)的抄袭文本
  • 测试结果:

屏幕截图 2025-09-23 133028

  • 测试覆盖率图

屏幕截图 2025-09-23 133021

6.模块部分异常处理
6.1文件不存在异常

  • 触发条件
    调用 FileHandler.readFile(String filePath) 时,传入的路径对应的文件不存在(如路径拼写错误、文件被删除、文件尚未创建)。
    // 检查文件是否存在 File file = new File(filePath); if (!file.exists()) { throw new FileNotFoundException("文件不存在: " + filePath); }

  • 单元测试案例

public class FileHandlerTest {

// 测试读取不存在的文件,预期抛出FileNotFoundException
@Test(expected = FileNotFoundException.class)
public void testReadFile_FileNotFound() throws Exception {
    // 传入一个肯定不存在的文件路径(临时目录下的non_exist.txt)
    String nonExistentPath = System.getProperty("java.io.tmpdir") + "/non_exist.txt";
    FileHandler.readFile(nonExistentPath);
}

6.2路径非文件异常
触发条件:调用 FileHandler.readFile(String filePath) 时,传入的路径是目录(而非文件),如传入 “C:/test/”(目录)而非“C:/test/orig.txt”(文件)
if (!file.isFile()) { throw new IOException("路径不是一个文件: " + filePath); }

  • 单元测试案例

public class FileHandlerTest {

// 测试读取目录路径(而非文件),预期抛出IOException
@Test(expected = IOException.class)
public void testReadFile_ReadDirectory() throws Exception {
    // 获取系统临时目录(肯定是目录,而非文件)
    String tempDirPath = System.getProperty("java.io.tmpdir");
    FileHandler.readFile(tempDirPath);
}

}

6.3空文本场景处理
触发条件:SimilarityCalculator.calculateSimilarity(String text1, String text2) 接收的文本为空(空字符串 "" 或 null);

  • 单元测试样例

public class SimilarityCalculatorTest {

// 测试空文本场景的相似度计算
@Test
public void testCalculateSimilarity_EmptyText() {
    // 场景1:两段空文本("" vs "")
    double simEmptyVsEmpty = SimilarityCalculator.calculateSimilarity("", "");
    assertEquals("两段空文本应视为完全相似(相似度1.00)", 1.00, simEmptyVsEmpty, 0.01);

    // 场景2:空文本 vs 非空文本("" vs "测试文本")
    double simEmptyVsNonEmpty = SimilarityCalculator.calculateSimilarity("", "测试文本");
    assertEquals("空文本与非空文本应视为完全不相似(相似度0.00)", 0.00, simEmptyVsNonEmpty, 0.01);

    // 场景3:null vs 非空文本(null vs "测试文本")
    double simNullVsNonEmpty = SimilarityCalculator.calculateSimilarity(null, "测试文本");
    assertEquals("null与非空文本应视为完全不相似(相似度0.00)", 0.00, simNullVsNonEmpty, 0.01);
}

}
处理逻辑:不抛出异常,返回符合业务逻辑的相似度结果:
两段文本均为空(或 null):返回 1.00(无内容差异,视为完全相似);
一段为空、一段非空:返回 0.00(无重叠内容,视为完全不相似)。
7.PSP实际花费时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 120 150
· Estimate · 估计这个任务需要多少时间 120 150
Development 开发 655 965
· Analysis · 需求分析 (包括学习新技术) 120 120
· Design Spec · 生成设计文档 30 50
· Design Review · 设计复审 15 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 15
· Design · 具体设计 60 80
· Coding · 具体编码 240 360
· Code Review · 代码复审 30 40
· Test · 测试(自我测试,修改代码,提交修改) 150 180
Reporting 报告 110 130
· Test Repor · 测试报告 60 60
· Size Measurement · 计算工作量 20 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 40
· 合计 985 1185
posted @ 2025-09-23 16:25  严展桐  阅读(12)  评论(0)    收藏  举报