exit
(1);
}
if(m.da_num*n.da_num){//若果结果为非零矩阵
for(inti=0;i!
=m.row;++i){//逐行相乘,得到Q各行的的结果
for(intii=0;ii!
=n.col;++ii)
ctemp[ii]=0;//每次temp都必须初始话为
Q.rops[i]=Q.da_num;//查找到一行的首个非零元素,找到就返回其值,否值就返回-,表示该行全为,不用相乘
//找到该行的最后一个非零元的下一个元素位置,注意,若该行为行(全为)的话,那么就该行首个非零元素
//就和最后一个非零元的下一个元素位置相同,那么直接跳出循环,不用相乘
if(i!
=m.row-1)
tp=m.rops[i+1];
else
tp=m.da_num;
for(intj=m.rops[i];j!
=tp;++j){//一行的各个三元组分别相乘累加
t_row=m.da[j].j;//该行某个元素找到与其相对应Matrix&n的行的首个非零元
if(t_row!
=n.row-1)//该行某个元素找到与其相对应Matrix&n的行的最后个非零元的下一位置
tr=n.rops[t_row+1];
else
tr=n.da_num;
//如果中对应的Matrix&n行不是行,则相乘累加
for(intk=n.rops[t_row];(k+1)&&k!
=tr;++k){
t_col=n.da[k].j;//找到结果所在列号
ctemp[t_col]+=m.da[j].e*n.da[k].e;//对应的行号和列号相等的元素相加后累加
}//fork;
}//求得Q中第i行的非零元。
//压入储存三元组结果
for(t_col=0;t_col!
=Q.col;++t_col)
if(ctemp[t_col]){
Q.da_num++;
dataDA(i,t_col,ctemp[t_col]);
Q.da.push_back(DA);
}
}
}
delete[]ctemp;
returnQ;
}
//-------------------------------------------------------------//
根据稀疏矩阵相乘算法的要求与题意,定义三元组数据类和矩阵类如下:
//--------定义数据类,储存三元组数据---------//
classdata{
public:
inti,j;
inte;
data(inti=0,intj=0,inte=0){
this->i=i;
this->j=j;
this->e=e;
}
voidset_data(inti,intj,inte){
this->i=i;
this->j=j;
this->e=e;
}
};
//-------------------------------------------------------------//
//-------—————定义矩阵类--------------//
classMatrix{
public:
vectorda;
introps[MAXSIZE];
introw,col,da_num;
Matrix(){
row=0;
col=0;
da_num=0;
}
Matrix(introw,intcol,intda_num=0){
this->row=row;
this->col=col;
this->da_num=da_num;
for(inti=0;i!
=da_num;++i)
rops[i]=-1;
}
voidput_data(datad){
if(!
da.size())
put_rops(d.i,da.size());
if(da.size()&&d.i!
=da[da.size()-1].i)
put_rops(d.i,da.size());
da.push_back(d);
}
voidput_rops(intm,intn){
rops[m]=n;
}
intget_number(intm,intn){
for(inti=0;i!
=da_num;++i){
if(da[i].i==m&&da[i].j==n)
returnda[i].e;
}
return0;
}
friendMatrixMult_Matrix(Matrix&m,Matrix&n);
};
//---------------------------------------------------------------//
运行结果:
本程序在LinuxGCC和WindosSP2VC++2008下均调试通过。
其运行结果如下:
程序结果分析:
分析算法的时间复杂度有如下结果:
累加器ctemp初始化的效率为O(A.da_num*B.da_num),求两矩阵相乘结果中的所有非零元的时间复杂度为O(M.col*N.col/N.row),进行压缩的时间复杂度为O(M.row*N.col),总的时间复杂度为O(M.row*N.col+M.col*N.col/N.row)。
实验任务二:
单词统计
问题描述:
统计输入文件中出现的不同的单词个数以及每个单词出现的频率,并起将这些单词按照词典的顺序排列好输出到文件中。
实验要求:
输入:
以文件的形式记录程序中所需要的数据。
输出:
结果应该存放在一个文件中,该文件的第一行是不同的单词个数,从第二行开始则为每个单词和其相应的频率,单词与频率之间用空格符分割,单词需按字典顺序排列。
算法设计与主要程序段:
问题分析:
本题的主要是单词的有序统计,主要难点在于如何设计高效的统计函数,达到广泛的统计要求(一般可统计数百万单词),首先是读入文件时的单词分割,然后是两个单词的比较(本题而言,可以重载“<”和“=”操作符),单词分割和两个单词的比较都比较简单,时间代价是线性的0
(1),故不需要优化。
只要是单词的统计上,如何去寻找好的数据结构和高效的算法。
首先可以想到数组,数组的优点是其有序性,故重载“<”和“=”操作符时可以利用二分查找法插入位置,时间代价为0(log(n)),但是在新的单词插入后,整个数组的序列会改变,这种元素移位操作却非常耗时,代价为O(n^2),再加上数组对统计单词数具有容量限制(数组的大小必须实现分配),就算用优化后的向量可以满足动态分配内存,但总的说来时间时间代价昂贵,不能采用。
再次是链表,链表优点可以动态分配内存,对单词的统计量没有限制,缺点是没有好的方法来实现重载“<”和“=”操作符,这个代价是0(n^2)。
当然优化后的List在排序时可以小一点,但时间效代价仍然非常昂贵,也不能采用。
考虑到前面两种数据结构各自的优缺点,可以直到数据结构中的二叉排序树(AVL)可以很好的解决动态储存空间的分配和插入查找,其时间代价为O(log(n)),已经非常廉价,但AVL在实现重载“<”和“=”操作符只能单向二分,为此可以对节点进行红黑标记,实现双向二分查找。
任务二流程图
查找与插入问题解决后,接下来就是单词与其个数之间的关系问题。
利用红黑数时,不妨可采用每个节点5个域,分别标记前驱,后继,红黑标记,键(单词),码(单词个数)。
这样的优点是可以将单词与其个数之间形成二元关系,即二元键值对。
通过“<”和“=”操作符运算,若一个单词(键)已经存在,只需将它的码值加1,若不存在,就在双向二分得到的位置插入新的结点,得到新的键值对。
当然,要实现一颗完好的红黑数是比较麻烦的,可以采用STL中对红黑优化过后的map。
经过以上分析,程序设计流程图如上所示:
其中单词分割程序为:
//--------------------将字符串转换成为单词----------------------//
boolchangeWords(string&s){
stringtemp;
inti=0;
intj;
while(!
((s[i]>='a'&&'z'>=s[i])||(s[i]>='A'&&'Z'>=s[i]))&&i!
=s.size())
++i;
j=i;
while(((s[j]>='a'&&'z'>=s[j])||(s[j]>='A'&&'Z'>=s[j])||(s[j]==39)))
++j;
if(i==s.size())
returnfalse;
for(;i!
=j&&i!
=s.size();++i){
if(s[i]<'a')
s[i]=s[i]+'a'-'A';
temp.push_back(s[i]);
}
s=temp;
returntrue;
}
//--------------------------------------------------------------//
单词计数数据结构map的选择:
//--------------------------------------------------------------//
mapcounters;
intsum=0;
strings;
while(in>>s){
if(changeWords(s)){
counters[s]++;
sum++;
}
}
//--------------------------------------------------------------//
运行结果:
本程序在LinuxGCC和WindosSP2VC++2008下均调试通过。
其运行结果如下:
程序结果分析:
进行单词统计的时候,只统计ASCII编码的单词,对于其他编码,如UNICLDE,GB2312,GBK,BIG5等编码的全角字母(占两个字节),全部滤过不做统计,对汉字及其他双字节的文字一律滤过,不做统计。
算法设计主要是利用map中红黑树,实现键值对,一个单词(键)对应一个值(值),形成映射.在查找和插入时主要是红黑数的双向二分查找插入,效率为O(log(size_of_map)),对于本程序的运用(统计量大约在10MB,1000000个单词以下来说,效率是可以接受的)。
如果需要进行更大的统计,则可以用hash表来实现编码。
查找和插入时间效率为O
(1)。
但是编码和处理地址冲突的指令较多,所以在统计少量的单词时,反而没有map快,同时由于统计对单词是有序要求,hash技术对于保持有序上很难做到,故另外还需要设计排序方案,但排序本身也是一个很耗时的事情,因而本题目的最佳选择还是map。
实验任务三:
指针式时钟
实验要求:
可视化的显一个指针式模拟时钟;可为程序设计一个美观大方的图标;通过菜单可以调整时间,定制指针式时钟的显示风格,比如指针、表盘的颜色、外形等,可以按照个人的兴趣进行其他的属性的扩展。
主要设计思路和所涉及的类:
本题是一个可视化编程问题,目前可视化编程工具比较多,我采用的是比较流行的VC++。
对于题目要求的可视化,可以建一个SDI(单文档视图结构)程序,设计主要分为可视化和控件标准两部分。
前一部分主要在Cview类中完成,后一部分主要在Cframe类中完成。
其中可视化画图主要在OnDraw函数中添加代码实现。
设计时首先是读取时间,现在读取时间的方式主要有从操作系统和网络远程读取,由于寝室上网不方便,我采用的是从操作系统中读取时间,然后由时间变量用三角函数转换得到各个指针的首末坐标,进而画出表盘,再添加计时器,每个一秒重新读取一次新时间,并更新表盘画面,这样就可以使闹钟动起来。
各个控件的属性与响应函数可以自己在Cframe类中添加代码完成。
按照以上的设计规划,可以将整个任务分为以下三块:
1.读取时间,以及记录闹钟的时间等,设定定时器。
2.画出表盘时钟,实现美观大方的可视化。
3.附加功能,如闹钟,日列,备忘录等控件设计。
对于第一块,先定义几个全局型变量
externboolifon;//闹钟标记
externboolifsound;//闹钟声音标记
externinth,m,s;//分别记录闹钟的时分秒
再设计计时器,使之每一秒钟向系统读一次时间。
代码如下:
intCAlarmClockView:
:
OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CFormView:
:
OnCreate(lpCreateStruct)==-1)
return-1;
//TODO:
Addyourspecializedcreationcodehere
//设置时间步长为1s.
SetTimer(1,1000,NULL);
return0;
}
接下来将重操作系统读取的UNIX时间节转化为时分秒数字时钟。
代码为:
CTimeNow=CTime:
:
GetCurrentTime();//读取操作系统的时间。
m_tDate=Now;
UpdateData(false);
m_dDate.SetToday(&Now);//将UNIX时间节转化数字形式
CStrings1,stime,ntime;
stime.Format("%d:
%02d:
%02d",h,m,s);//将闹钟时间格式化
//将UNIX时间节转化为时分秒数字时钟,并格式化
ntime.Format("%d:
%02d:
%02d",Now.GetHour(),Now.GetMinute(),Now.GetSecond());
第二块,有了以上读出的系统时间后,就可以根据时间变量来画出此时刻的半盘,代码如下:
//界面美观性的设计
if(Now.GetHour()<=11)s1="早上好,欢迎您使用Rolex情侣珍藏版AlarmClock!
";
elseif(Now.GetHour()<=13)s1="中午好,欢迎您使用Rolex情侣珍藏版AlarmClock!
";
elseif(Now.GetHour()<=18)s1="下午好,欢迎您使用Rolex情侣珍藏版AlarmClock!
";
elses1="晚上好,欢迎您使用Rolex情侣珍藏版AlarmClock!
";
pDC->SetTextColor(RGB(0,0,255));
pDC->TextOut(90,15,s1);
//为闹钟同时也设计界面
if(!
ifon)
{
m_Static1.ShowWindow(false);
pDC->SetTextColor(RGB(255,0,0));
pDC->TextOut(60,245,"闹铃功能未启动");
}
else
{
m_Static1.ShowWindow(true);
pDC->SetTextColor(RGB(255,0,0));
pDC->TextOut(60,245,"闹铃已启动,时间为"+stime);
}
pDC->SetTextColor(RGB(0,0,0));
pDC->TextOut(300,230,"现在时刻:
");
pDC->Rectangle(365,255,465,270);
pDC->SetTextColor(RGB(0,255,255));
pDC->TextOut(300,255,ntime);
CBrushbr,br1;
br.CreateSolidBrush(RGB(0,255,0));
pDC->SelectStockObject(NULL_PEN);
pDC->SelectObject(&br);
intl;
l=int(((double)Now.GetHour()+(double)Now.GetMinute()/60.0+(double)Now.GetSecond()/3600.0)/24.0*100);
pDC->Rectangle(366,256,366+l,270);
br1.CreateSolidBrush(RGB(255,255,255));
pDC->SelectObject(&br1);
//画表盘
intnCenterX=385;
intnCenterY=135;
CStringstrDigits;
inti,x,y;
CSizesize;
CPenPen(PS_SOLID,5,RGB(0,128,255));
CPen*pOldPen=pDC->SelectObject(&Pen);
pDC->Ellipse(300,50,470,220);
doubleRadians;
pDC->SetTextColor(RGB(0,0,0));
for(i=1;i<=12;i++)
{
strDigits.Format("%d",i);
size=pDC->GetTextExtent(strDigits,strDigits.GetLength());
Radians=(double)i*6.28/12.0;
x=nCenterX-(size.cx/2)+(int)((double)72*sin(Radians));
y=nCenterY-(size.cy/2)-(int)((double)72*cos(Radians));
pDC->TextOut(x,y,strDigits);
}
Radians=(double)Now.GetHour()+(double)Now.GetMinute()/60.0+(double)Now.GetSecond()/3600.0;
//画表盘主要利用三角函数进行坐标变化。
Radians*=2*3.14159/12.0;
CPenHourPen(PS_SOLID,5,RGB(233,233,15));
pDC->SelectObject(&HourPen);
pDC->MoveTo(nCenterX,nCenterY);
pDC->LineTo(nCenterX+(int)((double)(25)*sin(Radians)),nCenterY-(int)((double)(25)*cos(Radians)));
//画时间指针
Radians=(double)Now.GetMinute()+(double)Now.GetSecond()/60.0;
Radians*=2*3.14159/60.0;
CPenMinutePen(PS_SOLID,3,RGB(0,0,255));
pDC->SelectObject(&MinutePen);
pDC->MoveTo(nCenterX,nCenterY);
pDC->LineTo(nCenterX+(int)((double)(40)*sin(Radians)),nCenterY-(int)((do