问题描述
计算一篇英文文章里26个英文字母的状态转移矩阵,以及这篇文章含有的信息熵
成员 | 姓名 | 任务 |
---|---|---|
组长 | 马靓颖 | 搜集英文文章,整理整体思路,计算状态转移矩阵 |
组员 | 郭俊康 | 统计文章字母总数,帮助解决其他成员遇到的问题 |
组员 | 蒋旭宾 | 求解特征向量,计算信息熵 |
目录
(一)问题分析
-
首先统计文章里字母总数
-
计算状态转移矩阵P
把a字母后面出现a的概率记为aa,即a后面出现a的个数÷a的总个数,则矩阵P表示为:
aa ab ac … az
ba bb bc … bz
…
za zb zc … zz -
求该矩阵对应特征向量C
即求解 ( P T − E ) X = 0 (P^T-E)X=0 (PT−E)X=0的非0解(正常情况下只有一个,即该矩阵的秩为25,原矩阵 r a n k ( P ) = 26 rank( P)=26 rank(P)=26),再归一化 -
计算信息熵 H ∞ = C ∗ H ( P ) H_{\infty}=C*H(P) H∞=C∗H(P)
(二)问题求解
1.统计字母总数
郭俊康:
- 满意:通过编程实现了对任意一篇英文文章的自然语言熵的计算
- 不足:没有考虑非英文字符串的情况,情况考虑不全面
//C++ 统计字母总数
#include <iostream>
using namespace std;
int main() {
char ch;
char s_letter[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
char b_letter[26]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
int num[26]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
float f_num[26]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int Sum;
for (int i = 1; i <= 10000; i++) {
ch = cin.get();//读入文章
if(ch == '#')break;
for(int j=0;j<=25;j++)
{
if(ch==s_letter[j] || ch==b_letter[j]) num[j]++;
}
}
for(int i=0;i<=25;i++)
{
cout<<s_letter[i]<<"出现的个数为"<<num[i]<<endl;
}
for(int i=0;i<=25;i++)
{
Sum+=num[i]; //计算字母总数
}
for(int i=0;i<=25;i++)
{
f_num[i]=num[i]*(1.0)/Sum * 100; //计算字母出现的频率*100
}
cout<<"字母总数为:"<<Sum<<"\n";
输出结果:
2.计算状态转移矩阵
马靓颖:
- 满意:自行设计出了从字母aa到zz循环的算法,利用了字符、数字和字符串之间相加的性质;统计特定字符在字符串中的个数通过借鉴统计单个字符在字符串中的个数的思想,再加以改进得到本文算法
- 不足:最终输出结果P矩阵无法正常显示出来,但是检查了P的元素是有具体数值的,猜测是26*26矩阵过大,系统用了某种方法折叠了结果
- 解决方法:利用可以输出的26*26=676个单个数值,存储成一维数组代替原本打算的二维数组,在下一阶段的计算中再转化为矩阵
//Java 求状态转移矩阵
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class HH {
public static void main(String[] args) {
List<String> list = HH.getContent();//调用方法getContent读取txt文件
for (String s : list) {//对于文件里每一个字符s
s=s.toLowerCase();//把大写字母转化为小写字母
float [][] P = new float [26][26];//建立矩阵P
//建立循环计算文章中出现 aa,ab,...,ba,bb,...,zz 的个数
for (int i=0;i<26;i++){
for(int j=0;j<26;j++){
char a='a';//对列操作
char aj=(char)(a+j);//字符与数字相加把结果强制转换类型为字符型,即可从字符a得到字符b,c,...,z
String A=Character.toString(aj);//再转换为字符串型
char b='a';//对行操作
char bi=(char)(b+i);//同理
String B=Character.toString(bi);//同理
String AB = A+B;//字符串相加是直接拼接,即可得到aa,ab,...,zz字符串
String result=s.replace(AB,"");//把AB字符替换成空白
int num = (s.length()-result.length())/AB.length();//(原长度减-现有长度)/AB字符的长度=AB字符的个数
System.out.println(AB+" "+num);//输出每个字符串对应数量
P[i][j]=num;//把结果存在矩阵P中
}
}
System.out.println(P);//输出P
}
}
//封装一个方法,功能:读取txt文件
public static List<String> getContent() {
String fileName = "F:\\临时有用的东西\\信息论\\word.txt";
List<String> lineLists = null;
try {
lineLists = Files
.lines(Paths.get(fileName), Charset.defaultCharset())
.flatMap(line -> Arrays.stream(line.split("\n")))
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
return lineLists;
}
}
该部分只统计了个数,并没有直接计算概率,放在了下一部分中
输出结果:
(部分截图)
3.求解特征向量C和信息熵
蒋旭宾:
- 满意:进一步加深了自然语言熵的理解,对编程有一定的进步
- 不足:对于特征值和特征向量出现复数的情况并没有进行进一步了解
%MATLAB 求解特征向量和信息熵
clc;clear;
%%%%%%%%%导入数据
data=xlsread('E:\dataya.csv');
A=zeros(26,26);
%%
%将数据转化为矩阵
for i=1:26
for j=1:26
A(i,j)=data(i*(j-1)+j);
end
end
%%
SumX=sum(A,2);
%将矩阵每一行除以行总数
for i=1:26
for j=1:26
A(i,j)=A(i,j)/SumX(i);
end
end
%%
%求状态转移矩阵P
E=eye(26,26); %定义单位矩阵26*26
% rank(A)
B=A'-E; %B为A的转置矩阵-E
% rank(B)
[X,D]=eig(B); %求特征值与特征向量X为特征向量。D为特征值
D=diag(D); %将特征值单独取出
%去除出现虚部的特征值与特征向量
Real_D=zeros(1,26);
for i=1:26
if (imag(D(i))==0)&&(real(D(i))~=0)
Real_D(i)=D(i);
end
end
%提取特征值非复数的特征向量
real_X=zeros(26,26);
for i=1:26
if Real_D(i)~=0
real_X(:,i)=X(:,i)
end
end
%%
%进行归一化处理
V=sum(real_X);
T=real_X(:,1)/V(1)
%进行这一步的时候我们发现仅有一组特征向量的和非0 ,其它都为0,故仅取第一组。
% for i=1:26
% real_X=real_X(:,i)/V(i);
% end
%%
%求hp
Hp=zeros(1,26);
for i=1:26
for j=1:26
if A(i,j)==0
Hp(i)=Hp(i);
else
Hp(i)=Hp(i)-A(i,j)*log2(A(i,j));
end
end
end
%%
Hp*T
输出结果:
(部分截图)
(三)结论
根据本文用到的英语文章,2694个英文字母中含有的信息熵是2.5437