用C/C++自己写一个宋词生成器,气质一下子就上来了呀

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用C/C++自己写一个宋词生成器,气质一下子就上来了呀相关的知识,希望对你有一定的参考价值。

成果

先看一下我自动生成的宋词


一、模型设计

1.1 词频统计:

1.1.1 基本理论:

​ 利用计算机统计宋词中汉字出现的频率,并通过对大量语料的学习,利用宋词自身的特性,在经过大量语料学习后,使用在宋词当中出现频率较高的词语或者单字排列组合来生成宋词。

1.1.2 算法基本思想:

  • 按顺序获取宋词语料中中文汉字信息。
  • 按顺序依次截取一个中文字符,并统计相同的中文字符出现的频率,按顺序保存到文件中,直到语料被截取完毕。
  • 按顺序依次截取两个中文字符,并统计相同的中文字符出现的频率,按顺序保存到文件中,直到语料被截取完毕。
  • 按顺序依次截取三个中文字符,并统计相同的中文字符出现的频率,按顺序保存到文件中,直到语料被截取完毕。
  • 获取在宋词中,不同词牌所对应的宋词生成规律。
  • 从之前所获得的的一个、两个、三个中文字符中,分别抽取一定数量的出现频率较高的中文字符。
  • 按照 E 中所获得的规律,生成相应词牌所对应的宋词。

1.1.3 算法评价:

​ 由于此算法需要频繁查找已有的中文字符,所以使用哈希表作为存储结构,这样将每次查询的时间复杂度控制在 O(1),那么整个算法的复杂度在 O(n)的时间内即可完成。当然,使用哈希表的缺点便是,占用很大的内存资源。

1.2 宋词生成

1.2.1 生成规则定义

自己可以定义要输入的格式,例如

·220--22122222221222222223222220--2222322223222222322222220

其中:

字符代表含义
1此处应填入一个中文字符
2此处应填入两个中文字符
3此处应填入三个中文字符
0回车换行
-空格
其他字符该词牌中相应位置应该填入的符号

1.2.2 生成宋词

根据自己定义的宋词输出规则,按顺序读取规则,根据当前规则的不同含义 从词频前一百的词组中 选出当前词语输出。

二、系统设计

2.1 词频统计:

2.1.1 数据结构:

pair<string, int> Pair_StrInt
词频记录:
	string:中文字符 
	int:相对应的中文字符出现频率

// 定义诗词结构体
struct myNode 	
    string Chant; // 词牌名
    string Rules; // 格式
;

2.2.2 算法实现:

1、对用于生成词频的文件 Ci.txt,进行预处理。

  • 将所有 Ci.txt 中的中文汉字信息按顺序放入变量 Text 中。
  • 实现函数:void InitText(string _infile);

2、获取一个中文字的词频,并输出到文件。

  • 文本中文字体编码格式采用 GB2312,一个汉字需要两个字节来表示,所以遍历 Text,每次截取两个字节,并用哈希表来保存每次截取的一个中文字,记录出现的频率。遍历结束,将哈希表中保存的数据转移到 vector 中,进行排序,调用输出函数,按词频由大到小输出到文件中。

  • 实现函数:void getOneWord(string out1);

3、获取两个中文字的词频,并输出到文件。

  • 遍历 Text,每次截取四个字节(原因同上),并用哈希表来保存每次截取的两个中文字,记录出现的频率。遍历结束,将哈希表中保存的数据转移到 vector 中,进行排序,调用输出函数,按词频由大到小输出到文件中。

  • 实现函数:void getTwoWord(string out2);

4、获取三个中文字的词频,并输出到文件。
略,同上

5、遍历 Text,每次截取六个字节(原因同上),并用哈希表来保存每次截取的三个中文字,记录出现的频率。遍历结束,将哈希表中保存的数据转移到 vector 中,进行排序,调用输出函数,按词频由大到小输出到文件中。

  • 实现函数:void getThreeWord(string out3);

6、输出函数。

  • 从 vector 中将保存的数据输出。

  • 实现函数:void OutputFile(const vector<Pair_StrInt> &StrInt_Vec, string outfile)

7、生成词前的预处理。

  • 分别抽取词频为前 100 的一个、两个、三个中文字。并对所选定的词牌进行定义。

    字符代表含义
    1此处应填入一个中文字符
    2此处应填入两个中文字符
    3此处应填入三个中文字符
    0回车换行
    -空格
    其他字符该词牌中相应位置应该填入的符号
  • 实现函数:void makePoetry(string out1, string out2, string out3);

8、自动生成并输出的词。

  • 按照规则,以生成随机数在数组中定位中文字的方式自动生成并输出词。
  • 实现函数:void Poetry(string _strTmp);

2.2.3 算法评估:

  • 理论上,由于采用哈希表作为存储结构,故算法时间复杂度为 O(n)。

三、系统演示

输入文件:

  • Ci.txt

输出:

  • 统计一个中文字的词频的文件
  • 统计两个中文字的词频的文件
  • 统计三个中文字的词频的文件
  • 宋词生成

四、开发环境

操作系统:Windows 10
编程语言:C/C++
开发工具:Visual Studio 2017

五、源码

项目链接:
https://download.csdn.net/download/weixin_45525272/64813267
源代码

#include<iostream>
#include<functional>
#include<string>
#include<vector>
#include<map>
#include<unordered_map>
#include<fstream>
#include<sstream>
#include<algorithm>
#include <ctime>

using namespace std;

typedef long clock_time;	// 程序运行时间
typedef pair<string, int> Pair_StrInt;	// 词频记录:string中文字符,int相应中文字符出现频率
typedef vector<Pair_StrInt>::iterator Vec_Pair_StrInt_Itr;	  // 词频排序迭代器

#define ERROR_0 cerr<<"打开文件错误 !!!"<<endl;exit(1);	// 警告:打开文件错误
#define ERROR_1 cerr<<"无法识别 !!!"<<endl;exit(1);		// 警告:无法识别
#define Lim 100		// 用于生成古诗词的中文字符来源:词频为取前100的中文字符

string infile = "Ci.txt";		// 用于生成词频的中华古诗词文本
string outfile1 = "out1.txt";	// 统计一个中文字的词频的文件
string outfile2 = "out2.txt";	// 统计两个中文字的词频的文件
string outfile3 = "out3.txt";	// 统计三个中文字的词频的文件
string project_time = "project_time.txt";	// 存储整个程序所运行的时间的文件
string One_strArr[100];			// 用于生成古诗词,所用到的一个中文字符
string Two_strArr[100];			// 用于生成古诗词,所用到的两个中文字符
string Three_strArr[100];		// 用于生成古诗词,所用到的三个中文字符

ifstream filein;
ofstream fileout;
string Text;

/// 定义诗词结构体
struct myNode

	string Chant;	// 词牌名
	string Rules;	// 词格式
;

// 定义哈希类型的存储结构,每次查找的时间复杂度控制在 O(1)。
unordered_map<string, int> StrInt_Hash;


/// 预处理Ci.txt:将所有Ci.txt中的中文汉字信息按顺序放入变量Text 中
void InitText(string infile)

	// 打开文件
	filein.open(infile);
	if (!filein)
	
		ERROR_0;
	

	// 将整个文件读入 string : 流迭代器
	std::ostringstream tmp;
	tmp << filein.rdbuf();
	string text_tmp = tmp.str();

	// 判断是否为中文编码字符
	unsigned char str_judge;
	string str_tmp;

	int len = text_tmp.size();
	for (int i = 0; i < len; )
	
		str_judge = text_tmp[i];
		// 采用GB2312编码,所有中文字符的编码范围:[0xB0, 0xF7]
		if (str_judge >= 0xB0 && str_judge <= 0xF7)
		
			str_tmp = text_tmp.substr(i, 2);
			i += 2;
			Text += str_tmp;
		
		else
		
			++i;
		
	

	filein.close();
	filein.clear();


/// 输出统计词频数据到文件
void OutputFile(const vector<Pair_StrInt> &StrInt_Vec, string outfile)

	// 打开文件
	fileout.open(outfile);
	if (!fileout)
	
		ERROR_0;
	

	// 输入到文件
	vector<Pair_StrInt>::const_iterator pair_itr;
	for (pair_itr = StrInt_Vec.begin(); pair_itr != StrInt_Vec.end(); pair_itr++)
	
		fileout << pair_itr->first << "\\t" << pair_itr->second << endl;
	

	fileout.close();
	fileout.clear();


/// 比较函数
bool Pair_StrInt_Cmp(const Pair_StrInt& p0, const Pair_StrInt& p1)

	return p0.second > p1.second;


/// 获取一个中文字的词频
void getOneWord(string out1)

	string str_tmp;

	int str_len = Text.length();
	// 两个字符拼成一个中文字符,要获取一个中文字,所以 +2 递增。
	for (int i = 0; i < str_len; i += 2) 
		str_tmp = Text.substr(i, 2);
		StrInt_Hash[str_tmp] += 1;
	

	// 将哈希表里的数据放入临时 vector 中
	vector<Pair_StrInt> StrInt_Vec_tmp(StrInt_Hash.begin(), StrInt_Hash.end());
	StrInt_Hash.clear();

	// 按词频排序:从大到小
	std::sort(StrInt_Vec_tmp.begin(), StrInt_Vec_tmp.end(), Pair_StrInt_Cmp);

	// 输出到文件中
	OutputFile(StrInt_Vec_tmp, out1);
	StrInt_Vec_tmp.clear();


/// 获取两个中文字的词频
void getTwoWord(string out2)

	string str_tmp;

	int str_len = Text.length();
	// 两个字符拼成一个中文字符,要获取两个中文字,所以 +4 递增。
	for (int i = 0; i < (str_len - 2); i += 2) 
		str_tmp = Text.substr(i, 4);
		StrInt_Hash[str_tmp] += 1;
	

	// 将哈希表里的数据放入临时 vector 中
	vector<Pair_StrInt> StrInt_Vec_tmp(StrInt_Hash.begin(), StrInt_Hash.end());
	StrInt_Hash.clear();

	// 按词频排序:从大到小
	std::sort(StrInt_Vec_tmp.begin(), StrInt_Vec_tmp.end(), Pair_StrInt_Cmp);

	// 输出到文件中
	OutputFile(StrInt_Vec_tmp, out2);
	StrInt_Vec_tmp.clear();


/// 获取三个中文字的词频
void getThreeWord(string out3)

	string str_tmp;

	int str_len = Text.length();
	// 两个字符拼成一个中文字符,要获取两个中文字,所以 +6 递增。
	for (int i = 0; i < (str_len - 4); i += 2) 
		str_tmp = Text.substr(i, 6);
		StrInt_Hash[str_tmp] += 1;
	

	// 将哈希表里的数据放入临时 vector 中
	vector<Pair_StrInt> StrInt_Vec_tmp(StrInt_Hash.begin(), StrInt_Hash.end());
	StrInt_Hash.clear();

	// 按词频排序:从大到小
	std::sort(StrInt_Vec_tmp.begin(), StrInt_Vec_tmp.end(), Pair_StrInt_Cmp);

	// 输出到文件中
	OutputFile(StrInt_Vec_tmp, out3);
	StrInt_Vec_tmp.clear();


// 定义自动生成一首词的规则
/*
为自动生成一首词,先根据不同词牌名,定义不同的格式。
在此:
	1		代表		此处应填入一个中文字符
	2		代表		此处应填入两个中文字符
	3		代表		此处应填入三个中文字符
	0		代表		回车换行
	-		代表		空格
其他字符		代表		该词牌中相应位置应该填入的符号

据以上规则:
1)makePoetry(string out1, string out2, string out3) 函数对所选定的词牌进行定义。
2)Poetry(string _strTmp) 函数自动生成并输出的词。

*/

/// 自动生成词
void Poetry(string str_tmp)

	int len = str_tmp.length();
	int myRandom;
	srand((unsigned)(time(NULL)));
	for (int i = 0; i < len; i++)
	
		switch (str_tmp[i])
		
		case '1':
			myRandom = rand() % Lim;
			cout << One_strArr[myRandom];
			break;
		case '2':
			myRandom = rand() % Lim;
			cout << Two_strArr[myRandom];
			break;
		case '3':
			myRandom = rand() % Lim;
			cout << Three_strArr[myRandom];
			break;
		case '0':
			cout << '\\n';
			break;
		case '-':
			cout << "  ";
			break;

		default:
			cout << str_tmp.substr(i, 2);
			++i;
			break;
		
	
	cout << endl;


/// 生成词前的预处理
void makePoetry(string out1, string out2, string out3) 

	ifstream fin1, fin2, fin3;
	ofstream fout1, fout2, fout3;
	fin1.open(out1);
	if (!fin1)
	
		ERROR_0;
	
	fin2.open(out2);
	if (!fin2)
	
		ERROR_0;
	
	fin3.open(out3);
	if (!fin3) 
		ERROR_0;
	
	string strTmp;
	for (int i = 0; i < Lim; ++i) 
		getline(fin1, strTmp);
		One_strArr[i] = strTmp.substr(0, 2);
		getline(fin2, strTmp);
		Two_strArr[i] = strTmp.substr(0, 4);
		getline(fin3, strTmp);
		Three_strArr[i] = strTmp.substr(0, 6);
	

	myNode node0;
	node0.Chant = "念奴娇";
	node0.Rules = "·220--22,12,222。22,21:222。22,22,23。22,222。0--222,23,22。22,3222。22,23,22。22,222。0";

	string strTmp0 = "----" + node0.Chant + node0.Rules; // "----"表示为生成的词设置格式
	Poetry(strTmp0);
	//system("pause");


void Solve() 

	InitText(infile);

	ofstream fout;
	fout.open(project_time);
	clock_t myStart, myFinish;
	double totaltime;

	myStart = clock();	// 开始计时

	getOneWord(outfile1);	// 获取一个中文字的词频
	getTwoWord(outfile2);	// 获取两个中文字的词频
	getThreeWord(outfile3);	// 获取三个中文字的词频

	myFinish = clock();			// 计时结束
	totaltime = (double)(myFinish - myStart) / CLOCKS_PER_SEC;	// 计算出程序总的运行时间

	fout << "运行时间为: " << totaltime << " 秒。" << endl;
	fout.close();
	fout.clear();

	makePoetry(outfile1, outfile2, outfile3);	// 自动生成一首词


int main()

	Solve();
	return 0;

以上是关于用C/C++自己写一个宋词生成器,气质一下子就上来了呀的主要内容,如果未能解决你的问题,请参考以下文章

面试常问的 C/C++ 问题,你能答上来几个?

面试常问的 C/C++ 问题,你能答上来几个?

佳悦教你提升自身气质

写一下自己对《统计学习方法》的一点认识

[年终总结]这就是2016的我

Hexo+Github博客最简教程-Dockerfile自动搭建