Advanced-software-engineering-course-week2

Homework1: A Proposal for My Team Project

题目:证件照照相机

项目背景:

      当今社会,用到证件照的场合数不胜数,包括各种考试报名,升学入学,工作投简历,办签证等,都会被要求提供近期证件照,有时还会有尺寸大小要求。前几天,因为研究生网上报名时需要一张电子版证件照,还有大小要求,无奈只好四处奔走找到一家照相馆,他们在拍好之后也只是用PS做了扣图换背景等简单处理,只要电子版竟然要15块大洋,洗出来才20。后来我觉得,只要电子版的话,我们完全可以自己做出来。

市场调研:

      有了这个想法之后,我立刻做了一个调查,在apple store里搜索了相关的app,总共有6个,其中一个打开时闪退无法使用,还有一个在2013年已经停止更新,其余四个都是最近一年才发布上线的,体验较好的只有1个,功能考虑的很全面,但在抠图时边界处理的还不够好。想到我们研究院也有做关于人脸识别的研究,我觉得开发一个更加方便好用的app是很可行的。毕竟有需求,需求还未被满足,我们又有很好的资源和技术,综上,这应该是一个很好的项目。

功能描述:

1、拍照后自动识别人脸和背景,智能抠图换背景

2、可以进行适当的手工处理,包括背景的显示与擦除等

3、能够选择照片的尺寸(1寸、2寸等)和大小(比如30k--300k等)

4、可以进行轻微的补光、美白等

5、能保存或上传

6、基本功能如上,其他功能未完待续...

Homework2: the Nth Prime Program

实现:

采用线性筛法,并对其中的细节进行改进优化。
流程如下:首先输入想要求的第N个质数的N,例如1,000,000,然后根据质数定理,有公式Pn<n*ln(n) + n*ln(ln(n)),得到上界,进而得到筛法的初始范围,该上界公式要求n>5,所以n<=5的情况就单列出来,代码如下:
	switch (Nth)
	{
	case 1:
		return 2;
	case 2:
		return 3;
	case 3:
		return 5;
	case 4:
		return 7;
	case 5:
		return 11;
	default:
		break;
	}//因为求上界的公式要求 Nth>=6
	unsigned int n = Nth*log(Nth) + Nth*log(log(Nth));//求第N个质数的上界
	//cout << n << endl;
	unsigned int count = 0;//统计当前是第几个质数
	bool* isPrimes = new bool[n + 1];//true 表示对应的数是质数
先筛掉所有的偶数(除了2),之后的操作都只针对奇数:
	bool flag = true;
	isPrimes[2] = true;
	for (unsigned int i = 3; i < n + 1; i++)
	{
		isPrimes[i] = flag;
		flag = !flag;
	}//首先去掉偶数
	Prime_list[count++] = 2;//先把2写入result list
列表中剩下的最小的奇数断定是质数,紧接着去掉它的倍数,这里有一个技巧,因为合数一定可以分解成几个质数的乘积,所以只需将现在得到的质数的质数倍的数筛掉即可。这样做可以避免重复筛除和判断,可以提高速度。在这个过程中,每得到一个质数就计数,判断是不是要找的质数,若是则输出并return,否则,继续。
	for (unsigned int i = 3; i < n + 1; i += 2)//只判断奇数
	{
		if (isPrimes[i] == true)
		{
			Prime_list[count++] = i;
			if (count == Nth)
			{
				return i;
			}
		}
		//unsigned int comp = n / i;
		for (unsigned int j = 1; j < count&&i*Prime_list[j] <= n; j++)//j从1开始,即Prime_list[j]从3开始,因为偶数已经被筛出
		{															//Prime_list[j] <= n/i等价于判断i*Prime_list[j] <= n,但可以防止溢出,Prime_list[j] <= comp
			isPrimes[i*Prime_list[j]] = false;
			if (i == Prime_list[j])
			{
				break;
			}
		}
	}
实验结果:


找到第1,000,000个质数花了0.078s,结果正确

Homework3: Count the word frequency and Output the top N hot words

流程如下:

首先输入根目录和N,然后遍历根目录下的所有文件,保存每一个文件的全路径,然后循环遍历所有文件,进行适当处理,将文件中的每个单词作为一个map<string,int>的关键字储存,若map中有这个关键字则其值加1,否则新建该关键字,其值置1,由此实现count功能。最后将这个map按pair写入一个vector中,自定义一个排序规则,将其排序,最后输出值最大的前N个单词及其数量。

代码细节如下:

先得到所有文件的路径,函数原型如下:

void getTextPath(string& Root, vector<string>& textPathArr);

采用递归调用的方法,进行遍历,整个过程耗时约0.015s

	if ((hFile = _findfirst(path.assign(Root).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			//如果是目录,就迭代,继续向下寻找
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
				{
					getTextPath(path.assign(Root).append("\\").append(fileinfo.name), textPathArr);
				}
			}
			else
			{//如果不是,就加入textPathArr
				textPathArr.push_back(path.assign(Root).append("\\").append(fileinfo.name));
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}

然后利用得到的路径进行文件操作,得到word map,函数原型如下:

void getWordMap(string& path, map<string, int>& result);


调试中发现,这个遍历过程是整个工作中耗时最多的,因为涉及大量的文件读取操作。所以优化也全在这一部分,为了节约读取文件的时间,我试了几个方法,

v1,最开始的方法,也最朴素,就是每次读入一个单词,然后做一些处理,包括去掉特殊字符和标点符号以及转换成小写等,最后处理完大概需要35s

void getWordMap(string& path, map<string, int>& result)//v1,34.946s
{
	ifstream infile;
	infile.open(path);
	if (!infile)
	{
		std::cout << "File open failed!" << endl;
	}
	string str;
	while (infile >> str)
	{
		//cout << str << endl; break;
		str.erase(remove_if(str.begin(), str.end(), bind2nd(less_equal<char>(), -1)), str.end());//移除特殊字符
		str.erase(remove_if(str.begin(), str.end(), ispunct), str.end());//移除标点符号
		if (str.length() == 0)
		{
			continue;
		}
		transform(str.begin(), str.end(), str.begin(), tolower);//转换成小写,不区分大小写
		++result[str];
	}
	infile.close();
}

因为减少读取文件的次数可以加快速度,所以我试着将整个文件一次读入到缓存区,之后再从缓存区内读出,

void getWordMap(string& path, map<string, int>& result)//v2,fread()先全部读取成string类型再处理,38.984s
{
	std::ifstream t(path);
	std::stringstream buf;
	buf << t.rdbuf();
	//istringstream is(buf.str());
	string str;
	while (buf >> str)
	{
		//cout << str << endl; break;
		str.erase(remove_if(str.begin(), str.end(), bind2nd(less_equal<char>(), -1)), str.end());//移除特殊字符
		str.erase(remove_if(str.begin(), str.end(), ispunct), str.end());//移除标点符号
		if (str.length() == 0)
		{
			continue;
		}
		transform(str.begin(), str.end(), str.begin(), tolower);//转换成小写,不区分大小写
		++result[str];
	}
	
}

但结果并不理想,反而更慢了一点,究其原因应该是对string的操作太费时间,所以我开始转为操作char类型字符数组:

void getWordMap(string& path, map<string, int>& result)//v4,先全部读取成char类型再处理,28.97s->25.736s->25.204s->24.245
{
	ifstream infile;
	int len;
	infile.open(path);      // open input file  
	infile.seekg(0, ios::end);    // go to the end  
	len = infile.tellg();           // report location (this is the length)  
	infile.seekg(0, ios::beg);    // go back to the beginning  
	char* buf = new char[len];    // allocate memory for a buffer of appropriate dimension  
	infile.read(buf, len);       // read the whole file into the buffer  
	infile.close();                    // close file handle  
	string str;
	for (char*p = buf; p - buf < len; p++)
	{
		if (*p == ' ' || *p == '\t' ||*p == '\n' || *p == '\r' )
		{
			//string str(word);
			if (str.length() == 0)
			{
				continue;
			}
			++result[str];
			str.clear();
		}
		else
		{
			if (ispunct(*p)||*p<0)//移除标点符号和特殊字符
			{
				continue;
			}
			str += tolower(*p);//转换成小写,不区分大小写
		}

	}
}


这次我将文件这个读入成char型数组,然后从这个数组中一个一个提取出word,同时去掉特殊字符和标点符号,最终确实变快了不少,大约25s

最后就是排序了,因为vector有sort()函数,可以指定比较规则,所以先将map复制到一个vector中:

map<string, int>::iterator iter = result.begin();
	while (iter != result.end())
	{
		result_sorted.push_back(*iter);
		++iter;
	}

然后写了一个比较规则,使其按降序排列

bool operator() (const PAIR& P1, const PAIR& P2)  
	{
		return P1.second > P2.second;//降序
	}


最后就是调用vector的sort()进行排序,

sort(result_sorted.begin(), result_sorted.end(), comp());


这个排序还是很快的,测试发现大约0.1s即可完成。

最后是试验结果:



整个程序大约需要25s左右的时间

实验代码:

https://github.com/v-chixma/Advanced-software-engineering-course-Week2



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值