概率算法Word格式文档下载.docx
《概率算法Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《概率算法Word格式文档下载.docx(13页珍藏版)》请在冰豆网上搜索。
![概率算法Word格式文档下载.docx](https://file1.bdocx.com/fileroot1/2022-11/24/cf820411-f7f2-44c5-a60d-47e954199e51/cf820411-f7f2-44c5-a60d-47e954199e511.gif)
=0,c>
=0,d>
=m。
d称为该随机序列的种子。
下面我们建立一个随机数类RadomNumber,该类包含一个由用户初始化的种子randSeed。
给定种子之后,既可产生与之相应的随机数序列。
randseed是一个无符号长整型数,既可由用户指定也可由系统时间自动产生。
constunsignedlongmaxshort=65536L;
constunsignedlongmultiplier=1194211693L;
constunsignedlongadder=12345L;
classRandomNumber
{
private:
//当前种子
unsignedlongrandseed;
public:
//构造函数,缺省值0表示由系统自动产生种子
RandomNumber(unsignedlongs=0);
//产生0-n-1之间的随机整数
unsignedshortRandom(unsignedlongn);
//产生[0,1)之间的随机实数
doublefRandom(void);
};
RandomNumber:
:
RandomNumber(unsignedlongs)
if(s==0)
randseed=time(0);
else
randseed=s;
}
unsignedshortRandomNumber:
Random(unsignedlongn)
randseed=multiplier*randseed+adder;
return(unsignedshort)((randseed>
>
16)%n);
doubleRandomNumber:
fRandom(void)
returnRandom(maxshort)/double(maxshort);
函数Random在每次计算时,用线性同余式计算新的种子。
它的高16位的随机性较好,将randseed右移16位得到一个0-65535之间的随机整数然后再将此随机整数映射到0-n-1范围内。
对于函数fRandom,先用Random(maxshort)产生一个0-(maxshort-1之间的整型随机序列),将每个整型随机数除以maxshort,就得到[0,1)区间中的随机实数。
下面来看看数值概率算法的两个例子:
1.用随机投点法计算π
设有一半径为r的圆及其外切四边形,如图所示。
向该正方形随机投掷n个点。
设落入圆内的点在正方形上均匀分布,因而所投入点落入圆内的概率为πr^2/4r^2,所以当n足够大时,k与n之比就逼近这一概率,即π/4。
由此可得使用随机投点法计算π值的数值概率算法。
具体实现时,只需要在第一次象限计算即可。
doubleDarts(intn)
staticRandomNumberdart;
intk=0;
for(inti=1;
i<
=n;
i++){
doublex=dart.fRandom();
doubley=dart.fRandom();
if((x*x+y*y)<
1)
k++;
return4*k/double(n);
再简单举个舍伍德算法的例子。
我们在分析一个算法在平均情况下的计算复杂性时,通常假定算法的输入数据服从某一特定的概率分布。
例如,在输入数据是均匀分布时,快速排序算法所需的平均时间是O(nlogn)。
但是如果其输入已经基本上排好序时,所用时间就大大增加了。
此时,可采用舍伍德算法消除算法所需计算时间与输入实例间的这种联系。
在这里,我们用舍伍德型选择算法随机的选择一个数组元素作为划分标准。
这样既能保证算法的线性时间平均性能又避免了计算拟中位数的麻烦。
非递归的舍伍德型算法可描述如下:
template<
classType>
Typeselect(Typea[],intl,intr,intk)
staticRandomNumberrnd;
while(true){
if(l>
=r)
returna[l];
inti=l,j=l=rnd.Random(r-l+1);
Swap(a[i],a[j]);
j=r+1;
Typepivot=a[l];
while(true)
while(a[++i]<
pivot);
while(a[--j]>
if(i>
=j)
break;
if(j-l+1==k)
returnpivot;
a[l]=a[j];
a[j]=pivot;
if(j-l+1<
k)
k=k-j+l-1;
l=j+1;
r=j-1;
template<
TypeSelect(Typea[],intn,intk)
if(k<
1||k>
n)
throwOutOfBounds();
returnselect(a,0,n-1,k);
平时我们一般开始考虑的是一个有着很好平均性能的选择算法,但在最坏情况下对某些实例算法效率较低。
这时候我们用概率算法,将上述算法改造成一个舍伍德型算法,使得该算法对任何实例均有效。
不过在有些情况下,所给的确定性算法无法直接改造成舍伍德型算法。
这时候就可以借助随机预处理技术,不改变原有的确定性算法,仅对其输入进行随机洗牌,同样可以得到舍伍德算法的效果。
还是刚才的例子,换一种方法实现:
voidShuffle(Typea[],intn)
n;
intj=rnd.Random(n-i)+i;
在上文里,我们对概率算法中的数值概率算法以及舍伍德算法举例作了简要的介绍,希望能使大家对概率算法有一个初步的认识,并且将这种思想运用到自己平时的编程中。
importjava.util.*;
importjava.util.regex.*;
importjava.io.*;
publicclassUserTreeMap...{
publicstaticvoidmain(Stringargs[])throwsIOException...{
BufferedReaderbuf=newBufferedReader(newFileReader("
english.txt"
));
System.out.println("
ReadunderthisdirEnglish.txt"
);
StringBuffersbuf=newStringBuffer();
//缓冲字符串
Stringline=null;
while((line=buf.readLine())!
=null)...{
sbuf.append(line);
//追加到缓冲字符串中
}
buf.close();
//读取结束
Patternexpression=Ppile("
[a-zA-Z]+"
//定义正则表达式匹配单词
Stringstring1=sbuf.toString().toLowerCase();
//转换成小写
Matchermatcher=expression.matcher(string1);
定义string1的匹配器
TreeMapmyTreeMap=newTreeMap();
//创建树映射存放键/值对
intn=0;
//文章中单词总数
Objectword=null;
//文章中的单词
Objectnum=null;
//出现的次数
while(matcher.find())...{//是否匹配单词
word=matcher.group();
//得到一个单词-树映射的键
n++;
//单词数加1
if(myTreeMap.containsKey(word))...{//如果包含该键,单词出现过
num=myTreeMap.get(word);
//得到单词出现的次数
Integercount=(Integer)num;
//强制转化
myTreeMap.put(word,newInteger(count.intValue()+1));
else
{
myTreeMap.put(word,newInteger
(1));
//否则单词第一次出现,添加到映射中
统计分析如下:
"
t文章中单词总数"
+n+"
个"
具体的信息在当前目录的result.txt文件中"
BufferedWriterbufw=newBufferedWriter(newFileWriter("
result.txt"
Iteratoriter=myTreeMap.keySet().iterator();
//得到树映射键集合的迭代器
Objectkey=null;
while(iter.hasNext())...{//使用迭代器遍历树映射的键
key=iter.next();
bufw.write((String)key+"
+myTreeMap.get(key));
//键/值写到文件中
bufw.newLine();
bufw.write("
english.txt中的单词总数"
english.txt中不同单词"
+myTreeMap.size()+"
bufw.close();
}
packagea;
importjava.io.BufferedReader;
importjava.io.BufferedWriter;
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.OutputStream;
importjava.io.OutputStreamWriter;
importjava.util.Iterator;
importjava.util.Map;
importjava.util.Set;
importjava.util.TreeMap;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
publicclassWordProbability{
FilesourceFile=newFile("
/Users/liu/Documents/news.rtf"
publicvoidcount()throwsIOException{
FileInputStreamfin=newFileInputStream(sourceFile);
BufferedReaderbr=newBufferedReader(newInputStreamReader(fin));
Stringstr=br.readLine();
String[]s=str.split("
"
TreeMap<
String,Integer>
map=newTreeMap<
();
for(inti=0;
i<
s.length;
i++){
Stringkey=s[i].toLowerCase();
if(s[i].length()>
1){
if(map.get(key)==null){
map.put(key,1);
else{
intvalue=map.get(key).intValue();
value++;
map.put(key,value);
111111*********111111
前几天读到google研究员吴军的数学之美系列篇,颇有感触。
而恰好自己前段时间做了个基于统计语言模型的中文切分系统的课程项目,于是乎,帖出来与大家共同学习。
分词技术在搜索引擎,信息提取,机器翻译等领域的重要地位与应用就不敖述了。
步入正题:
)
<
!
--[if!
supportLists]-->
一、<
--[endif]-->
项目概述
本切分系统的统计语料是用我们学校自己开放的那部分,大家可以在这里下载,中文字符约184万,当然这都是已切分好了的,可以用此建立一个比较小的语料库。
本系统我主要分下面四个步骤完成:
1、<
语料预处理
2、<
建立2-gram(统计二元模型)
3、<
实现全切分
4、<
评估测试
下面我分别对这四个方面一一道来。
下载的已切分的语料都是形如“19980131-04-012-001/m现实/n的/u顿悟/vn却/d被/p描/v出/v形/Ng来/v。
/w”,有的前面还保留了日期编号,因为这些切分语料的来源是人民日报。
预处理主要是按标点符号分句,句子简单定义为(。
?
!
:
;
)这五种标点符号结尾的词串,句子首尾分别添加<
BOS>
和<
EOS>
这两个表示句子开始和结束的标记,这在2-gram建模时要用的,后面会提到。
处理过程中,忽略词类信息和前面的日期信息,因为我这个切分系统不考虑词类标注。
如前面这句预处理后应该为下面形式“<
现实的顿悟却被描出形来。
”,当然切分词之间你可以用你想用的符号标记,而不必是空格。
因为考虑到所有的英文字符和数字的ASCII,我用了下面方法实现之:
out;
//输出流
in;
//输入流
StringBuffers1=newStringBuffer();
//缓冲
chara=in.read();
while(a!
=-1)//判断是否已到流的终点
if((a=='
。
'
||a=='
:
;
))//一句结束
Strings2=newString(s1);
out.write("
//在句子前加<
out.write(s2);
//在句子末尾加<
out.write('
/n'
//换行
s1=newStringBuffer();
elseif(a=='
/'
)
s1=s1.append((char)32);
//分词位置空格
elseif(a>
256)
s1=s1.append((char)a);
a=in.read();
out.close();
in.close();
建立2-gram模型(统计二元模型)
在这里首先简单介绍一下n-gram模型和2-gram模型。
根据语言样本估计出的概率分布P就称为语言L的语言模型。
对给定的句子s=w1w2…wn,(数字,n,i都为下标,wi为句子s的一个词)。
由链式规则(Chainrule),P(s)=p(w1)p(w2|w1)p(w3|w1w2)……p(wn|w1w2w3…w(n-1)),对p(wi|w1w2…w(i-1))而言,(w1w2…w(i-1))即为wi的历史。
考虑前面n-1个词构成历史的模型即为n-gram模型。
n越大,提供的语境信息也越多,但代价就越大,且需训练语料多;
n较小时,提供的信息比较少,但计算代价小,且无需太多训练语料。
令c(w1,…,wi)表示词串w1,w2…wi在训练语料中出现的次数,则由最大似然估计,P(wn|w1,…,w(n-1))=c(w1,…,wn)/c(w1,…,w(n-1)).同理,则2-gram为P(wn|w(n-1))=c(w(n-1),wn)/c(w(n-1)).
若想了解更多相关知识,大家找相关资料看看,随便把大学时的那本概率与统计课本拿出来翻翻,数学真是一个好东东:
回归项目:
)训练语料一共有5万多个不同的词。
建立2-gram统计模型时不断要把每个词在训练语料中出现频率统计出来,还要把每个词及其后面的那个词组成的2-gram在训练语料中出现频率统计出来。
因为在切分时会频繁的在建立的2-gram模型中查找相关的数据,所有,存储这个2-gram模型数据的数据结构一定要能提供高效的查找。
故选择hash表,它能提供常数时间的查找。
Java类库里提供了HashMap类,基于数据两还不是非常大,故可直接拿来用。
在存储时,每一个key值对应一个在训练语料中出现过的词语,而每一个key值对应的value值又是一个HashMap。
暂且称为子hashmap.这个结构有点类似文件结构里的二级索引。
其相关代码如下:
怎么在预处理文件里把词分别读出来就不罗嗦了,方法:
每读入一行,按空格分成String数组,用个正则表达式匹配下即能得到。
//此方法传入的两个词组成一个2-gram,prewd为前一个词,currwd为紧随其后的词
publicstaticvoidadd(Stringprewd,Stringcurrwd){
Stringkey=prewd;
Stringcurr=currwd;
booleanbb=HMap.containsKey(key);
//Hmap是一个已存在的HashMap,用来存储2-gram统计模型。
在这里判断preword是否在主map中
if(bb==false){//若主map中无,则添加
HashMaphm=newHashMap();
//首先,新构造一个子MAP
hm.put(key,newInteger
(1));
//存储主KEY的频率hm.put(curr,newInteger
(1));
//存储主KEY后面紧接着的那个词频率
HMap.put(key,hm);
//将主KEY和对应的子MAP放入主MAP中
else//若主map中含有该词
HashMaptemp=(HashMap)HMap.get(key);
//返回主KEY所对应的子MAP,进行值的修改
intcount=((Integer)temp.get(key)).intValue()+1;
//在子map中将主key次数加1
temp.put(key,newInteger(count));
if(temp.containsKey(curr))//判断子map中是否含有该词
intvalue=((Integer)temp.get(curr)).intValue()+1;
temp.put(curr,newInteger(value));