第二次作业

`| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023 |

| ----------------- |--------------- |

| 这个作业要求在哪里| https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13324 |

| 这个作业的目标 | <用psp查重论文> |`

我的GitHub链接:https://github.com/dio688/DIO688/tree/main/3123004277px
psp表格

PSP阶段 预估耗时(分钟) 实际耗时(分钟)
计划
估计时间 480
开发
需求分析 60 40
生产设计文档 30 10
设计复审 20 20
代码规范 20 40
具体设计 60 60
具体编码 120 80
代码复审 30 50
测试 60 50
报告
测试报告 30 30
计算工程量 10 10
事后总结与改进 30 30
总计 500 480

代码

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
public static void main(String[] args) {
String file1 = "orig.txt";
String file2 = "orig_add.txt";
String resultFile = "result.txt";

    try {
        double similarity = calculateCosineSimilarity(file1, file2);
        writeResultToFile(resultFile, similarity);
        System.out.println("重复率计算完成!");
    } catch (IOException e) {
        System.out.println("计算重复率时出现错误:" + e.getMessage());
    }
}

public static double calculateCosineSimilarity(String file1, String file2) throws IOException {
    // 读取文件内容
    String content1 = readFileContent(file1);
    String content2 = readFileContent(file2);

    // 分割字符串为单词
    String[] words1 = content1.split("\\s+");
    String[] words2 = content2.split("\\s+");

    // 计算词频向量
    int[] vector1 = calculateWordFrequency(words1);
    int[] vector2 = calculateWordFrequency(words2);

    // 计算余弦相似度
    double dotProduct = calculateDotProduct(vector1, vector2);
    double magnitude1 = calculateMagnitude(vector1);
    double magnitude2 = calculateMagnitude(vector2);

    return dotProduct / (magnitude1 * magnitude2);
}

private static String readFileContent(String file) throws IOException {
    StringBuilder content = new StringBuilder();
    BufferedReader reader = new BufferedReader(new FileReader(file));
    String line;

    while ((line = reader.readLine()) != null) {
        content.append(line).append(" ");
    }

    reader.close();
    return content.toString();
}

private static int[] calculateWordFrequency(String[] words) {
    int[] frequency = new int[words.length];

    for (int i = 0; i < words.length; i++) {
        frequency[i] = 1;

        for (int j = i + 1; j < words.length; j++) {
            if (words[i].equals(words[j])) {
                frequency[i]++;
                words[j] = "";
            }
        }
    }

    return frequency;
}

private static double calculateDotProduct(int[] vector1, int[] vector2) {
    double dotProduct = 0;

    for (int i = 0; i < vector1.length; i++) {
        dotProduct += vector1[i] * vector2[i];
    }

    return dotProduct;
}

private static double calculateMagnitude(int[] vector) {
    double magnitude = 0;

    for (int value : vector) {
        magnitude += Math.pow(value, 2);
    }

    return Math.sqrt(magnitude);
}

private static void writeResultToFile(String file, double similarity) throws IOException {
    BufferedWriter writer = new BufferedWriter(new FileWriter(file));
    writer.write("重复率: " + similarity);
    writer.close();
}

}
结果


  1. 计算模块接口的设计与实现过程
    代码组织
    类:Main 类负责程序的入口和主要逻辑。

函数:

main:程序入口,调用计算相似度和写入结果的函数。

calculateCosineSimilarity:计算两个文件的余弦相似度。

readFileContent:读取文件内容。

calculateWordFrequency:计算词频向量。

calculateDotProduct:计算向量点积。

calculateMagnitude:计算向量模长。

writeResultToFile:将结果写入文件。

函数关系
main 调用 calculateCosineSimilarity 和 writeResultToFile。

calculateCosineSimilarity 调用 readFileContent、calculateWordFrequency、calculateDotProduct 和 calculateMagnitude。

关键函数流程图
calculateCosineSimilarity:

读取文件内容。

分割内容为单词。

计算词频向量。

计算余弦相似度。

算法关键
余弦相似度:通过计算两个向量的夹角余弦值来衡量相似度,值越接近1,相似度越高。

独到之处:直接基于词频向量计算,简单高效。

  1. 计算模块接口部分的性能改进
    性能改进思路
    优化词频计算:使用 HashMap 存储词频,减少时间复杂度。

并行处理:对大规模数据,使用多线程并行计算。

性能分析图
工具:使用 JProfiler 进行性能分析。

消耗最大函数:calculateWordFrequency,因涉及双重循环。

性能改进时间
约2小时。

  1. 计算模块部分单元测试展示
    单元测试代码import org.junit.Test;
    import static org.junit.Assert.*;

public class MainTest {
@Test
public void testCalculateCosineSimilarity() throws IOException {
String file1 = "test1.txt";
String file2 = "test2.txt";
double expected = 0.75;
double actual = Main.calculateCosineSimilarity(file1, file2);
assertEquals(expected, actual, 0.01);
}

@Test
public void testReadFileContent() throws IOException {
    String file = "test.txt";
    String expected = "hello world";
    String actual = Main.readFileContent(file);
    assertEquals(expected, actual);
}

}
测试数据构造
test1.txt 和 test2.txt:包含部分相同和不同单词的文本。

test.txt:包含简单文本。

  1. 计算模块部分异常处理说明
    异常设计目标
    IOException:处理文件读写错误。

NullPointerException:处理空文件或无效路径。

单元测试样例

@Test(expected = IOException.class)
public void testIOException() throws IOException {
Main.calculateCosineSimilarity("nonexistent.txt", "nonexistent2.txt");
}

@Test(expected = NullPointerException.class)
public void testNullPointerException() throws IOException {
Main.calculateCosineSimilarity(null, null);
}

总结
设计:代码组织清晰,函数职责明确。

性能改进:通过优化数据结构和并行处理提升性能。

单元测试:覆盖主要功能,确保代码正确性。

异常处理:处理文件读写和路径错误,增强鲁棒性。

posted @ 2025-03-08 20:09  PengXxx  阅读(38)  评论(0)    收藏  举报