统计文本词频--建立树状结构

引文

统计一个文本中的单词数量,相信大家不感到陌生,在不考虑性能的情况下,程序可以很简单。获取一个单词,再与已有的单词库一个一个做对比,对比相同时,将次数加1;对比到最后一个也没有找到相同单词时,则添加到末尾,并赋次数为1。文本小时很可取,简单方便直观。但是当文本增大时呢?这样线性的对比,10M的文本就足够让程序运行半个钟头了。

以下先介绍Python的解决方法

 

#Py3.4
import sys
try:
    fin = open('mission.txt','r')
except:
    print ("Can't open the file!")
    sys.exit(1)
Words = {}
for line in fin:
    line = line.rstrip()    #Strip '\n'
    line = line.lower()
    line = line.split(' ')
    for word in line:
        if word not in Words:
            Words[word] = 1
        else:
            Words[word] = Words[word] + 1
fin.close()
for word in Words:
    print (word,Words[word])



建立字典,单词作为键,出现次数作为与键对应的值。先获取单词,再与字典中的单词对比,当获取到的单词在字典的键中,则出现次数加1,当键不在字典中,就添加键/值对。由于Python对字典的键检索做足了优化,所以以上程序性能很好,处理10M文本小问题。“小布”让它处理了一个172MB的文本,总共花了21s(Py3.4+win7)。

以下介绍建立树状结构的处理方案。

正文

单词的长度是有限的,一般不会太长,并且经过大小写转换后,只会由26个字母和符号'表示(it's)。所以可以用单词中的一个字母作为树的一个节点,每个单词都是从树根到树叶的一条路径。每一个节点可以如下定义:

一个节点后面有a~z,'  共27种可能的节点(注意按照这个顺序),在代码中可以如下实现:

#define N 27                             //26个字母加一个'字符

struct alphabet
{
    char letter;                         //保存当前节点字母
    string spell;                        //保存此路径拼写
    int times;                           //保存此拼写次数,若times=-1,后面就不再有节点
    alphabet *next[N];                   //当前节点下的分枝
}

 

例如单词"word"和"world",可以建立如下树状图(Head仅作为头节点,真正的单词从第二层开始)

上图中层次最深为:最长单词+1 (层)。将单词的拼写作为建树的依据,这样能够省时的关键在于:这样的树,每个单词都分别有一个固定的位置,对于一个单词,不是通过一一对比获取它的位置,而是通过计算得来的,例如单词"me",长度为2,只要计算两次就能找到他的位置。m为字母表第13个字母,e为字母表第5个字母,则me在树上的位置为Head->next[12]->next[4]。word在树上的位置为:Head->next[22]->next[14]->next[17]->next[3]。

"小布"同样让它处理了一个172MB的文本,总共花了59s(VS2013+win7)。程序是半年前之写的,当时是按字节读取文本,即每次读取一个字节,就要将输入流入栈,转而处理该字节,而Python的程序是一次读取一行文本,在输入上性能就优于C++的程序了。在引文部分说的线性对比方式,也编写了一个程序,"小布"没敢用172M文本作为测试,因为处理一个6.5M的文本就用了12+分钟,172M == OMG。

什么?还要赋源程序?拿不出手怎么办...

 

#include<iostream>
#include<fstream>
#include<string>
using namespace std;

#define MAX 50                           //默认最长单词50个字符
#define N 27                             //26个字母加一个'字符

unsigned long points = 0;

struct alphabet
{
	char letter;                         //保存当前节点字母
	string spell;                        //保存此路径拼写
	int times;                           //保存此拼写次数,若times=-1,后面就不再有节点
	alphabet *next[N];                   //当前节点下的分枝
}*head, *tail, *now = NULL, *point[MAX];

unsigned long statistic();
void search(char str[MAX]);
void cr8_link(char letter);
void put_del(ofstream &output, int i);

void main()
{
	int i = 0;
	unsigned long num = 0;                //记录总共词汇数量,最大2^64=1.844674407371×10^19个词汇
	head = tail = new alphabet;
	head->letter = 'A';                   //头字母记为大写,保证不可能匹配上
	head->spell = "The statistical number of word is";
	for (int i = 0; i<N; i++)                //将head指向的所有节点赋值为空
		head->next[i] = NULL;
	num = statistic();                    //开始统计任务
	if (num == 0) return;
	head->times = num;                    //总词数放在head指向的times中
	now = head;                           //从头开始输出
	ofstream output;
	output.open("result.txt");
	output << "The number of points is:" << points << "\n";
	put_del(output, 0);                   //输出结果于result.txt中
	output.close();
}

unsigned long statistic()
{
	int i = 0;
	unsigned long num = 0;                //记录总词汇数
	register char c, ch = '\0';
	char str[MAX];                        //保存一个单词的拼写
	ifstream input;
	input.open("mission.txt");
	if (!input)
	{
		cerr << "Please rename the file as\"mission.txt\",then try again latter\n";
		return 0;
	}
	while (!input.eof())
	{
		i = 0;
		if (ch >= 'A'&&ch <= 'Z')  { ch += 32; str[i] = ch; i++; ch = '\0'; }            //ch中保存的数据使用后一定置0,防止下次再次使用
		else if ((ch >= 'a'&&ch <= 'z')) { str[i] = ch; i++; ch = '\0'; }
		while (!input.eof())
		{
			c = input.get();
			if (c >= 'A'&&c <= 'Z')  { c += 32; str[i] = c; i++; }
			else if ((c >= 'a'&&c <= 'z') || (c == '\'')) { str[i] = c; i++; }
			else break;
		}
		str[i] = '\0';
		while (!(input.eof()) && ((c = input.get())<'A' || (c>'Z'&&c<'a') || (c>'z')));  //跳过后面的空格、回车和标点,循环结束时,到达文本末尾或读进一个字母
		if (!input.eof()) ch = c;         //如果未到达文本末尾,则读进一个字母,保存
		if (i != 0)                       //如果i的值未改变,则首次输入为空格或回车
		{
			num++;                    //否则输入为单词,单词总数加1
			search(str);
		}
	}
	input.close();
	return num;
}
void search(char str[MAX])
{
	int i = 0;
	tail = now = head;                        //从头开始查找
	while (str[i] != '\0')                    //本单词遍历每个字母才代表本单词的枝建立完全
	{
		while ((now != NULL) && (str[i] != '\0'))
		{
			tail = now;               //tail保存now上一节点,判断now下移后是否空
			if (str[i] == '\'')       //字符'的ASCII码为39,对应节点数为26
				now = now->next[str[i++] - 13];  //39-13=26,正好对'的节点索引
			else
				now = now->next[str[i++] - 97];  //str[i]-97正好是字母对应0~26的节点索引
		}
		if (now == NULL)
			cr8_link(str[i - 1]);     //出循环两个口,如果now为空,即该建立相应的节点
	}
	now->spell = str;                         //本单词遍历每个字符,说明本枝建立完全,在节点处保存拼写以便输出
	now->times++;
}

void cr8_link(char letter)                        //在当前位置添加一个字母节点
{
	points++;
	now = new alphabet;                       //now指向新节点
	for (int i = 0; i<N; i++)                 //节点后27个节点指针指向空
		now->next[i] = NULL;
	now->times = 0;
	now->letter = letter;
	if (letter == '\'') letter = '{';         //对字符'的特殊处理
	tail->next[letter - 97] = now;            //有tail保存上一节点,建立的新节点应该跟在tail后面
}

void put_del(ofstream &output, int i)             //采用递归输出结果
{
	point[i] = now;                           //point[i]保存now经过的节点路径,能够原路返回,实现按序输出
	if ((now->times != 0) && (now != NULL))   //次数不为0的节点才是单词,次数为0的节点只是路径
		output << now->spell << ":" << now->times << endl;
	for (int j = 0; j<N; j++)                 //对于一个节点point[i],后面有27个节点
	{
		now = point[i]->next[j];          //now遍历point[i]的27个节点
		if (now != NULL)                  //如果now非空,则now又作为一个节点,后面有27个节点
		{
			put_del(output, i + 1);
			delete now;               //递归回来时,数据都已经输出,直接删除节点
		}
	}
}


 

 

 

 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值