程序设计方法专题实验报告.docx
- 文档编号:9221533
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:25
- 大小:309.22KB
程序设计方法专题实验报告.docx
《程序设计方法专题实验报告.docx》由会员分享,可在线阅读,更多相关《程序设计方法专题实验报告.docx(25页珍藏版)》请在冰豆网上搜索。
程序设计方法专题实验报告
程序设计方法
专题实验
实验任务
实验任务一:
矩阵乘积
问题描述:
已知矩阵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的选择: //--------------------------------------------------------------// map 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
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 程序设计 方法 专题 实验 报告