第一次个人编程作业

发布时间:2022-07-05 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了第一次个人编程作业脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
软件工程 网工1934班级链接
作业要求 作业要求链接
作业目标 1. 尝试个人开发一个论文查重项目2. 学会使用 PSP 表格预计和记录各模块开发时间3. 学会使用单元测试对项目进行测试4. 学习使用性能分析工具来找出代码中的性能瓶颈并进行改进5. 学会使用 GITHub 来管理代码和测试用例

作业 GitHub 链接@H_304_27@

一、PSP 表格

PSP 2.1 PErsonal Software PRocess Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· EstiMATE · 估计这个任务需要多少时间 240 360
Development 开发
· Analysis · 需求分析 (包括学习新技) 240 300
· Design Spec · 生成设计文档 30 45
· Design review · 设计复审 10 5
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 45
· Design · 具体设计 300 190
· Coding · 具体编码 150 160
· Code Review · 代码复审 45 30
· test · 测试(自我测试,修改代码,提交修改) 120 300
Reporting 报告
· Test Report · 测试报告 30 25
· Size Measurement · 计算工作量 10 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 15 10
· 合计 1240 1690

二、模块接口的设计与实现

模块设计

  • 主类
    • Main 类:main(),传入文件绝对路径,计算两文本内容重复率,并写入答案文件。
  • 工具类
    • WorDFreq 类:记录分词后每个词的词频。
    • Article 类:对文件内容的封装,包含了文件名、原始字符串、分词结果以及词频集合。
    • TxtIOUtils 类:readTxt(),读取txt文件内容;writeTxt(),向txt文件写入内容。
    • TextTools 类:getSegmentList(),分词操作;getWordFrequency(),统计词频;getArticle(),封装成 Article.
    • CompareTask 类:对两 Article 对象生成词频向量,TF-iDF 加权,计算 Jaccard 系数,即两文本内容重复率。
    • CompareReport 类:生成两文本重复率的报告。
  • 依赖库
    • hanlp:用于分词。
    • guava:用于统计词频时用到的 MultiSet 支持。
    • junit:用于单元测试。

项目结构

第一次个人编程作业

关键函数

getSegmentList()

第一次个人编程作业

getWordFrequency()

第一次个人编程作业

关键算法

广义 Jaccard 系数

前几天上数据挖掘课的时候,讲到相似性度量的时候,书中就提到“广义 Jaccard 系数”。

广义 Jaccard 系数,元素的取值可以是实数。又称为 Tanimoto 系数,用EJ来表示,计算方式如下: EJ(A,B) = (A * B) / (|A|^2 + |B|^2 - A * B) 其中 A、B 分别表示为两个向量,集合中每个元素表示为向量中的一个维度,在每个维度上,取值通常是 [0, 1] 之间的。

TF-IDF

对于两篇文档,分词之后,形成两个“词语--词频向量”,词语可以做为 EJ 的维度,如何将词频转换为实数值。借鉴 tf/idf 的思路。对于每个词语,有两个频度:1.在当前文档中的频度;2. 在所有文档中的频度。其中 1 相当于 tf ,与权重正相关;2 相当于 df,与权重反相关。对于 1,权重就可以取词频本身 tf(w) = D(w),D(w)表示在当前文档中 w 出现的次数。对于2,计算权重为 idf (w) = LOG(totalWC/C(w)). C(w)是词语 w 在所有文档中出现的次数,TotalWC 是所有文档中所有词的总词频。

三、模块接口部分的性能改进

最初采用 tika 来获取 txt 文件内容,发现大材小用了,并且要增加 apache 的许多外部依赖包,导致程序打包体积过大,性能也有所降低。后来就简单地使用 IO 来读写字符串。 Jprofiler 的性能分析图:

第一次个人编程作业

从上图来看,程序消耗最大的属于 HanLP 中的函数,及主要消耗来自分词操作。

四、模块部分单元测试展示

测试用例在 3119005373 文件夹下。

public class ProcessTest {

	private static String root = "3119005373/";

	@Test
	/*
	  测试分词
	 */
	public void segmentTest() {
		String str = "今天是星期天,天气晴,今天晚上我要去看电影。";
		List<Term> segmentList = TextTools.getSegmentList(str);
		System.out.println(segmentList);
	}

	@Test
	/*
	  测试统计词频
	 */
	public void wordFreqTest() {
		String str = "今天是星期天,天气晴,今天晚上我要去看影。";
		List<Term> segmentList = TextTools.getSegmentList(str);
		List<WordFreq> wordFreqList = TextTools.getWordFrequency(segmentList);
		System.out.println(wordFreqList);
	}

	@Test
	/*
	  测试getArticle
	 */
	public void getArticleTest() throws IOException, TextException {
		Article article = TextTools.getArticle(root + "test_orig.txt");
		System.out.println(article.getName());
		System.out.println(article.getText());
		System.out.println(article.getSegmentList());
		System.out.println(article.getWordFreqList());
	}

	@Test
	/*
	  测试CompareTask和CompareReport
	 */
	public void compareTest() throws IOException, TextException {
		// TODO 涉及读写可用多线程FutureTask优化。
		Article a1 = TextTools.getArticle(root + "test_orig.txt");
		Article a2 = TextTools.getArticle(root + "test_plag.txt");
		CompareTask task = new CompareTask(a1, a2);
		task.execute();
		CompareReport report = task.getCompareReport();
		System.out.println(report);
	}

	@Test
	/*
	  报告多个测试
	 */
	public void compareMultiTest() throws IOException, TextException {
		List<CompareTask> tasks = new ArrayList<>();

		Article a1 = TextTools.getArticle(root + "orig.txt");
		Article b1 = TextTools.getArticle(root + "orig_0.8_add.txt");
		tasks.add(new CompareTask(a1, b1));
		System.out.println("load 1");

		Article a2 = TextTools.getArticle(root + "orig.txt");
		Article b2 = TextTools.getArticle(root + "orig_0.8_del.txt");
		tasks.add(new CompareTask(a2, b2));
		System.out.println("load 2");

		Article a3 = TextTools.getArticle(root + "orig.txt");
		Article b3 = TextTools.getArticle(root + "orig_0.8_dis_1.txt");
		tasks.add(new CompareTask(a3, b3));
		System.out.println("load 3");

		Article a4 = TextTools.getArticle(root + "orig.txt");
		Article b4 = TextTools.getArticle(root + "orig_0.8_dis_10.txt");
		tasks.add(new CompareTask(a4, b4));
		System.out.println("load 4");

		Article a5 = TextTools.getArticle(root + "orig.txt");
		Article b5 = TextTools.getArticle(root + "orig_0.8_dis_15.txt");
		tasks.add(new CompareTask(a5, b5));
		System.out.println("load 5");

		for (CompareTask task : tasks) {
			task.execute();
			System.out.println(task.getCompareReport().toString());
			System.out.println();
		}
	}

}

测试覆盖率截图:

第一次个人编程作业

主方法因为会退出程序终止测试,所以没加入单元测试中。

五、模块部分异常处理说明

文件不存在的异常

@Test
/*
测试文本文件不存在的异常
*/
public void fileNotExistTest() throws TextException {
	try {
		Article article = TextTools.getArticle(root + "test.txt");
	}catch (IOException e) {
		e.printStackTrace();
	}
}

测试场景:3119005373 文件夹中不存在 test.txt 文件。

文本为空的异常

@Test
/*
测试文本内容为空的异常
*/
public void emptyTextTest() throws IOException {
	try {
		Article article = TextTools.getArticle(root + "test_empty.txt");
	}catch (TextException e) {
		e.printStackTrace();
	}
}

测试场景:test_empty.txt 为空。

答案文件已存在且不可写的异常

@Test
/*
测试答案文件已存在且不可写的异常
*/
public void fileReadOnlyTest() {
	try {
		String str = "写不进去啊";
		TxtIOUtils.writeTxt(str, root+"test_readonly.txt");
	}catch (IOException e) {
		e.printStackTrace();
	}
}

测试场景:test_readonly.txt 设置只读。

六、参考

  1. https://blog.csdn.net/xceman1997/article/details/8600277
  2. https://www.cnblogs.COM/TtTiCk/Archive/2007/08/04/842819.htML
  3. https://gitee.com/nanbowang/HanLP-textSimilarity

脚本宝典总结

以上是脚本宝典为你收集整理的第一次个人编程作业全部内容,希望文章能够帮你解决第一次个人编程作业所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。