程序设计方法专题实验报告.docx

上传人:b****7 文档编号:9221533 上传时间:2023-02-03 格式:DOCX 页数:25 大小:309.22KB
下载 相关 举报
程序设计方法专题实验报告.docx_第1页
第1页 / 共25页
程序设计方法专题实验报告.docx_第2页
第2页 / 共25页
程序设计方法专题实验报告.docx_第3页
第3页 / 共25页
程序设计方法专题实验报告.docx_第4页
第4页 / 共25页
程序设计方法专题实验报告.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

程序设计方法专题实验报告.docx

《程序设计方法专题实验报告.docx》由会员分享,可在线阅读,更多相关《程序设计方法专题实验报告.docx(25页珍藏版)》请在冰豆网上搜索。

程序设计方法专题实验报告.docx

程序设计方法专题实验报告

 

程序设计方法

专题实验

 

实验任务

实验任务一:

矩阵乘积

问题描述:

已知矩阵A,B,

当A的列数和B的行数相同时,则A与B可以相乘,其乘积为一个m*p的矩阵D:

其中dij=

(i=1,2…,m;j=1,2…,p)。

简记为D=A*B,其中:

已知矩阵A,B,C中大多数元素为0,这种矩阵称为稀疏矩阵,可采用三元组表示矩阵的的i行第j列的值为a,其他未列出的元素的值均为0,在计算机中,可以用行优先法给出稀疏矩阵中的非0元素的三元组,首先是第1行按列给出,然后是第2行按列给出……

例如矩阵:

那么该矩阵的三元组表示为:

111

232

24-1

321

332

343

实验要求:

编程完成计算D=A*B*C第i行第j列的值。

输入文件说明:

第一行:

x,y两个正整数,分别表示输出结果在矩阵D中的行和列。

第二行:

m,n,o,p,表示A为矩阵m×n矩阵,B为n×o矩阵,C为o×p矩阵。

第三行及以后各行是:

i,j,a,表示矩阵的三元组表示法中的一个元素的值,每个矩阵之间有一个空行。

矩阵的表示顺序为A,B,C。

………………

注:

1≤m,n,o,p≤6000,即三元组的总个数不大于6000.数据之间用空格分开。

输出文件:

一行,为D=A*B*C的第x行y列元素的值。

算法设计及主要程序:

问题分析:

本题的主要考虑两个方面,一是稀疏矩阵的压缩储存,二是两个稀疏矩阵之间的乘法。

其中最重要的一步又是矩阵的三元组乘法。

任务一流程图

(1).根据矩阵相乘的定义有:

在经典算法中,不论

的值是否为0,都要进行一次乘法,而实际上,这两者有一个值值为0时,其积也为0。

因此,在对稀疏矩阵进行相乘运算时,应该免去这种无效操作,为求Q的值,只需在M.data中和N.data中找到对应元素(即M.data中的j与N.data中的i相等的元素)相乘即可。

(2).这样相乘的基本操作是:

对于M中的每个元素M.data[p](p=1,2,3……,M.da_num),找到N中所有满足M.data[p].j=N.data[p].i的元素N.data[q],求得其乘积。

由于矩阵Q中每个元素的值是个累计和,这个乘积M.data[p].v×N.data[p].v只是Q[i][j]的一部分。

为了便于操作,应当对每个元素设计一个累计和变量,其初值为0,然后扫描数组M,求得相应元素的乘积并累加到适当的求和累计和的变量上。

(3).两个稀疏矩阵相乘的乘积不一定为零矩阵。

而两个即使矩阵的分量不为0,而乘积也可能是0。

因此乘积矩阵Q中的元素是否为非零元,只有在求得其累加和后才能得知。

由于Q中元素的行号和M中的行号一致,由此可对Q进行逐行处理,先求得累计求和的中间结果(Q的一行),然后再压缩到Q.data中去。

在解决了稀疏矩阵三元组相乘后,其储存结构也就迎刃而解了。

只需够造一个三元组类记录三元组的属性(行号,列号,值),在构造矩阵类的时候加入属性举证行数,列数,三元组非零元总数,行优先标记数组就可。

根据以上分析,程序设计流程图如上面所示:

其中最重要的矩阵的三元组乘法程序如下:

//---------两稀疏矩阵相乘的主要算法,定义为友元函数--------------//

MatrixMult_Matrix(Matrix&m,Matrix&n){

MatrixQ(m.row,n.col);

int*ctemp=newint[n.col];

inttp,t_row,tr,t_col;

if(m.col!

=n.row){

cout<<"两矩阵不能相乘!

"<

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

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工作范文 > 行政公文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1