第一次个人编程作业

软件工程 网络工程1934
这个作业要求在哪里 个人项目
这个作业的目标 学会做一个项目的各个步骤

Github目录
不知道为什么不能添加tags和release,初步判断是网络问题导致不能上传(毕竟用的烂电脑)。
现在已经在学校上传了。

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

由于昨天(19号)比完赛之后还帮别人干活,计划有点错乱,而且现在工作的电脑不在手上,很不容易才借到了一台电脑,网络还要贼差劲,文件根本上传不了。
这导致了这个博客(和项目)还需要一点时间完善一下,现在看到的还是一个不完整的博客。
收工啦~

模块接口的设计与实现过程

设计思路

说明一下程序流程,有三个函数,分别是文本切分、特征构建和量度距离,主函数获得文件内容后,先将两个字符串进行文本切分(省略空白字符),然后对得出来的结果放入特征构建之中,再把得到的数组分别进行量度,最后进行简单的运算即可得出相似度。

文本切分

预计原型:

std::vector<std::string> textSegmentation(std::string s)

传入字符串,返回其文本切分后的结果,暂定使用unigram进行分词,视情况也会使用bigram。

特征构建

预计原型:

std::vector<int> vecConstruct(std::vector<std::string>)

传入文本切分后的数组,返回一个向量,暂定使用独热表示方法,即每一个词使用一个独特的长度为1的向量,各向量正交,也就是无关。

量度距离

预计原型:

double calcDistance(std::vector<int>)

传入特征向量,返回其离原点的距离,欧拉距离或者余弦距离都行。

主函数

主函数需要处理参数的文件名,读入字符串,文本切分后对全部文本进行离散化,还有最后通过距离计算相似度。

实现过程

文本切分

都用n-gram分词了还要想啥?直接暴力。
优点:简单易懂,毕竟看了一下这么随便的样例就没必要深度学习了吧。
时间复杂度:\(O(n)\)
空间复杂度:\(O(1)\)

std::vector<std::string> textSegmentation(std::string s)
{
	vector<string> res;
	int n(s.size());
	if (n == 1) res.push_back(s.substr(0, 1));
	for (int i = 0; i < n - 1; ++i)
	{
		res.push_back(s.substr(i, 2));	//bigram
	}
	return res;
}

特征构建

两个文本都有个桶装着所有词,还有一个集合记录所有词,每一个词独享一个维度,遍历各个桶每个词二分一下找到对应的维度+1即可,最后返回的就是该文本的特征向量。
优点:还是简单易懂,毕竟我比较萌新。
时间复杂度:\(O(nlogn)(离散化)+O(nlogn)(查询+处理)\)
空间复杂度:\(O(n)\)
离散化

	for (auto it : vsori) refer.push_back(it);
	for (auto it : vsdis) refer.push_back(it);			                //join two set
	std::sort(refer.begin(), refer.end());

	refer.erase(std::unique(refer.begin(), refer.end()), refer.end());		//Discretization

查询+处理

std::vector<int> vecConstruct(std::vector<std::string> vec)
{
	vector<int> res;
	res.resize(refer.size(), 0);
	for (auto it : vec)
	{
		int id(std::lower_bound(refer.begin(), refer.end(), it) - refer.begin());	//get the position of 1 of this charater
		if (id >= 0 && id < res.size()) ++res[id];
	}
	return res;
}

量度距离

欧拉距离,简单来说就是所有基数平方的和开方。

\[d_i(a_i, r_i) = \sqrt{\sum^{n}_{j = 1}{(a_i - r_i)^2}} \]

时间复杂度:\(O(n)\)
空间复杂度:\(O(1)\)
代码不用了吧,这不是是个人都会?

主函数

主要是文件读入输出和异常处理,都是些零碎的东西。

性能改进

全局效能

overall
可以看出,时间主要是花费在了std::vector和std::string上,毕竟用的是STL库嘛,方便得来也要付出一点代价的,尝试打开O2优化,还要删除一些琐碎的东西(离散化写复杂了,导致new()和delete()的用量比预期的多了两倍以上)。

文本划分

textSeg
占用时间最长的部分,因为大量的使用了std::string operator new(),STL库和分配空间速度都比较慢,导致了常数很大。

特征构建

vecCons
虽然说这部分时间复杂度是最高的,但是因为样例的数据量比较小,\(logn (< 15)\) 比上述的常数小得多,所以时间上比较快。

量度距离

calcs
速度最快的一部分,毕竟这里没有怎么用到STL库,而且都是简单的算术运算。

改进后效能

已打开氧气优化
overall_fix
可以看到实际上是快了一倍有多的,这里没有开堆分析,开了堆分析之后是600ms,我发现这个对性能影响很大,不过测试和实际运行也不太一样。
newdel_fix
从30%降到了5%左右,可能随着内存的增大,速度会越来越慢。
fun_fix
预估实际运行效果应该差不多,计算部分仍然是<1ms。

单元测试

单元测试的代码放在Github了,因为还没学会如何在单元测试里面给main函数传递参数,所以只分别测试了每一个函数,一共有6个单元测试。
这里提一下其中一个错误。
outOfRange
std::vector operator [] 越界了,给更改向量增加了一个限制条件后通过了:
UnitTest
还有个问题就是VS里用不了中文,如果使用GBK编码的话在测试环境里会乱码,如果使用UTF-8的话就不能用标点符号。所以需要用到比对的都用了英文样例而不是中文样例。

异常处理

其实我直接放在了main里了,毕竟我不大会用cpp的try-catch块(我才不会说cpp里的函数能不throw就不throw呢)。不过这样的话有一些异常应该我没能想到,Java还是挺方便的说。
放一下部分异常处理的代码,错误样例在Github里的Example目录下,写了个批处理文件执行程序。

	if (argc < 4)					//Exception: Not enough Arguments.
	{
		puts("Not Enough Arguments!");
		return 0;
	}
	else if (argc > 4)				//Exception: Too many Arguments.
	{
		puts("Too Many Arguments!");
		return 0;
	}
	std::ifstream fori(argv[1]);
	if (!fori.is_open())			//Exception: file 1 cannot open.
	{
		printf("%s is not exist or cannot open!",argv[1]);
		return 0;
	}
	std::ifstream fdis(argv[2]);
	if (!fdis.is_open())			//Exception: file 2 cannot open.
	{
		printf("%s is not exist or cannot open!", argv[2]);
		return 0;
	}
	std::ofstream fout(argv[3]);
	if (!fdis.is_open())			//Exception: file 3 cannot open.
	{
		printf("%s is not exist or cannot open!", argv[3]);
		return 0;
	}
	if (vsori.empty())			//Exception: file 1 is empty.
	{
		printf("%s is empty!", argv[1]);
		return 0;
	}
	if (vsdis.empty())			//Exception: file 2 is empty.
	{
		printf("%s is empty!", argv[2]);
		return 0;
	}
        if (ori_0 < 0.0000001) ans = 0.0;	//Exception: divide 0

bat
批处理文件的源代码,是有那么一丁点难看的啦……

rem this is a test list.

rem test: No Argument.
C:\Users\juseice\Desktop\streamtest\main.exe
rem test: No output file.
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f0.txt C:\Users\juseice\Desktop\streamtest\s0.txt
rem test: Wrong input file name.
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f5000.txt C:\Users\juseice\Desktop\streamtest\s0.txt C:\Users\juseice\Desktop\streamtest\ans0.txt
rem test: Empty file.
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f0.txt C:\Users\juseice\Desktop\streamtest\s0.txt C:\Users\juseice\Desktop\streamtest\ans0.txt
rem test 1:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f1.txt C:\Users\juseice\Desktop\streamtest\s1.txt C:\Users\juseice\Desktop\streamtest\ans1.txt
rem test 2:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f2.txt C:\Users\juseice\Desktop\streamtest\s2.txt C:\Users\juseice\Desktop\streamtest\ans2.txt
rem test 3:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\f3.txt C:\Users\juseice\Desktop\streamtest\s3.txt C:\Users\juseice\Desktop\streamtest\ans3.txt
rem test 4:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\orig.txt C:\Users\juseice\Desktop\streamtest\orig_0.8_add.txt C:\Users\juseice\Desktop\streamtest\ans_orig_0.8_add.txt
rem test 5:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\orig.txt C:\Users\juseice\Desktop\streamtest\orig_0.8_del.txt C:\Users\juseice\Desktop\streamtest\ans_orig_0.8_del.txt
rem test 6:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\orig.txt C:\Users\juseice\Desktop\streamtest\orig_0.8_dis_1.txt C:\Users\juseice\Desktop\streamtest\ans_orig_0.8_dis_1.txt
rem test 7:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\orig.txt C:\Users\juseice\Desktop\streamtest\orig_0.8_dis_10.txt C:\Users\juseice\Desktop\streamtest\ans_orig_0.8_dis_10.txt
rem test 8:
C:\Users\juseice\Desktop\streamtest\main.exe C:\Users\juseice\Desktop\streamtest\orig.txt C:\Users\juseice\Desktop\streamtest\orig_0.8_dis_15.txt C:\Users\juseice\Desktop\streamtest\ans_orig_0.8_dis_15.txt

pause
posted @ 2021-09-20 01:00  juseice  阅读(110)  评论(1)    收藏  举报