《数据结构》课程设计报告多项式计算.docx
《《数据结构》课程设计报告多项式计算.docx》由会员分享,可在线阅读,更多相关《《数据结构》课程设计报告多项式计算.docx(15页珍藏版)》请在冰豆网上搜索。
《数据结构》课程设计报告多项式计算
《数据结构》课程设计报告
姓名
学号
2009级
7组
实验室:
提交日期
成绩
指导教师
实验题目:
多项式计算器
问题解析(对问题的分析、理解和解题方法):
我们先对数据结构进行了分析,对其运算进行了详细的分解。
我们把整个程序我们分为6个部分加减法,乘法,除法,排序整理,多项式解析,输入输出的格式及用户界面。
一·
1.多元多项式加法:
直接加,进行排序整理。
2.多元多项式减法即加法的取反运算。
3.如何进行解析是一个难点,我们中序表达式转后序表达式的方法进行解析。
二·
4.多元多项式乘法是前一个多项式的每一项与后一项的每一项相乘相加,然后排序整理。
5.由于上述分析中提到对多项式进行排序整理,则我们就要先写一个sort()的函数,对多项式进行排序整理。
其原理是按照其中一个字母的次数的降次排序,使用的是冒泡算法。
三·
6.一元多项式除法即比较前一项与后一项的最高次数,若被除数的最高次数大于等于除数的最高次数,则被除数的用最高次项除以除数的最高次项,得到一个临时的商.再用被除数减去这个商乘以除数的积作为新的被除数。
如此循环,就可以得到商。
7.一元多项式的模,模就是除法中最后的被除数。
8.对整个多项式进行输入输出的界面比较容易设计,但是对多项式的输出格式很难调试,有很多情况(去符号,去0,指数是0的情况等等)。
数据结构选择、算法设计:
我们用java语言编辑程序,应为它里面含有很多可以直接引用的的方法。
多项式用List(线性表)存储,其中每一个节点为一个单项式,单项式内部有系数和指数,系数为一个double型数字,指数用一个TreeMap(可排序映射表)表示,字符串存单项式的字母,对应的值为该字母的指数.
多项式内部封装了加减乘除模运算和转换为字符串,作为支持,又封装了getMaxExp(取最高次数),sort(排序整理,去0项),negate(取反).这些计算需要用到单项式的计算,所以也对单项式封装了多种方法。
单项式封装了add(加),multiple(乘)divide(除),compareTo(比较),remove0(去除指数为0的项),isNegative(判断是否为负数项),转换为字符串。
另外还有解析运算类,用于把字符串解析成多项式和运算符,然后通过多项式类进行运算,这样的好处就是程序入口简单,降低耦合性,便于代码复用.
用户界面单独编写,并通过解析运算类进行多项式处理,返回结果。
用户界面只有输入框,输出框和一个按钮,用法简单,报错功能完善。
1.中缀表达式转后缀表达式:
·开始扫描;
·数字和字母时,转换成多项式加入后缀表达式;
·运算符:
a.若为最高级的运算符,入栈;
b.若为'(',入栈;
c.若为')',则依次把栈中的的运算符加入后缀表达式中,直到出现'(',从栈中删除'(';
d.若为不是最高级的运算符,则将从栈顶到第一个优先级不大于(小于,低于或等于)它的运算符(或'(',但优先满足前一个条件)之间的运算符加入后缀表达式中,该运算符再入栈.
2.用后缀表达式计算
·构建一个空堆栈,扫描后缀表达式;
·多项式时,入栈;
·运算符时,从堆栈中弹出两个多项式运算,将结果再次入栈;
·最后的栈顶元素就是结果多项式.
任务分工及进度计划:
任务分工:
我的任务是解析字符串,按顺序转换成后缀表达式,并调用多项式的计算.以及编辑多项式的加法和减法运算。
进度:
我们在8.24下午进行分组,大家讨论整个程序的大体构架,并且进行任务的划分。
8.25白天在各自的程序完成后,进行合并以及大量的调试,晚上把输入输出界面编辑出来了,并开始写了各自的实验报告。
8.26早上交付程序并提交实验报告。
用户手册:
1.在用户界面的第一行输入你想要计算的多项式。
2.用户要记住多元多项式只能计算加,减,乘法而不能进行除法和模运算;一元多项式可以进行加,减,乘,除,模运算。
3.输入完毕后,请检查多项式是否输入错误。
4.检查完毕后,请按“计算”按钮,你会在下一行得出你想要运算的多项式的结果。
测试结果:
1.一元多项式加法:
2.一元多项式减法:
3.一元多项式乘法:
4.一元多项式除法:
5.一元多项式模运算:
6.多元多项式加法:
7.多元多项式减法:
8.多元多项式乘法:
程序清单:
解析并计算:
packagepolynomial;
importjava.util.ArrayList;
importjava.util.HashSet;
importjava.util.List;
importjava.util.Set;
importjava.util.Stack;
publicclassEval
{
publicPolyeval(Stringexp)
{
List
returndoEval(list);//真正求值
}
//查找字母个数(元数)
publicintcountEle(Stringexp)
{
Setele=newHashSet();
for(inti=0;i{
if(Character.isLetter(exp.charAt(i)))
ele.add(exp.charAt(i));
}
returnele.size();
}
//遇到操作符压栈,遇到表达式从后缀表达式中弹出两个数,计算出结果,压入堆栈
privatePolydoEval(List
{
Stack
Objectelement;
Polyn1,n2,result;
try
{
for(inti=0;i{
element=list.get(i);
if(elementinstanceofPoly)
{
stack.push(element);
}
elseif(isOperator((String)element))
{
n2=(Poly)stack.pop();
n1=(Poly)stack.pop();
result=doOperate(n1,n2,(String)element);
stack.push(result);
}
}
return(Poly)stack.pop();
}
catch(RuntimeExceptione)
{
thrownewIllegalExpressionException(e.getMessage());
}
}
privatePolydoOperate(Polyn1,Polyn2,Stringoperator)
{
if(operator.equals("+"))
returnn1.add(n2);
elseif(operator.equals("-"))
returnn1.subtract(n2);
elseif(operator.equals("*"))
returnn1.multiply(n2);
elseif(operator.equals("/"))
returnn1.divide(n2);
elseif(operator.equals("%"))
returnn1.mod(n2);
else
returnn1.power(n2);
}
privatebooleanisOperator(Stringstr)
{
returnstr.equals("+")||str.equals("-")||str.equals("*")
||str.equals("/")||str.equals("%")||str.equals("^");
}
privateListinfixExpToPostExp(Stringexp)
{//将中缀表达式转化成为后缀表达式
ListpostExp=newArrayList();//存放转化的后缀表达式的链表
StringBuffernumBuffer=newStringBuffer();//用来保存一个数的
StackopStack=newStack();//操作符栈
charch,preChar;
opStack.push('#');
try
{
for(inti=0;i{
ch=exp.charAt(i);
switch(ch)
{
case'+':
case'-':
case'*':
case'/':
case'%':
case'^':
preChar=opStack.peek();
//如果栈里面的操作符优先级比当前的大,则把栈中优先级大的都添加到后缀表达式列表中
while(priority(preChar)>=priority(ch))
{
postExp.add(""+preChar);
opStack.pop();
preChar=opStack.peek();
}
opStack.push(ch);
i++;
break;
case'(':
//左括号直接压栈
opStack.push(ch);
i++;
break;
case')':
//右括号则直接把栈中左括号前面的弹出,并加入后缀表达式链表中
charc=opStack.pop();
while(c!
='(')
{
postExp.add(""+c);
c=opStack.pop();
}
i++;
break;
//#号,代表表达式结束,可以直接把操作符栈中剩余的操作符全部弹出,并加入后缀表达式链表中
case'#':
charc1;
while(!
opStack.isEmpty())
{
c1=opStack.pop();
if(c1!
='#')
postExp.add(""+c1);
}
i++;
break;
//过滤空白符
case'':
case'\t':
i++;
break;
//数字则凑成一个整数,加入后缀表达式链表中
default:
if(Character.isDigit(ch))
{
while(Character.isDigit(ch))
{
numBuffer.append(ch);
ch=exp.charAt(++i);
}
Polynum=newPoly(Integer.parseInt(numBuffer
.toString()));
postExp.add(num);
numBuffer=newStringBuffer();
}
elseif(Character.isLetter(ch))
{
postExp.add(newPoly(ch+""));
i++;
}
}
}
}
catch(RuntimeExceptione)
{
thrownewIllegalExpressionException(e.getMessage());
}
returnpostExp;
}
privateintpriority(charop)
{//定义优先级
switch(op)
{
case'+':
case'-':
return1;
case'*':
case'/':
case'%':
return2;
case'^':
return3;
case'(':
case'#':
return0;
}
thrownewIllegalExpressionException("Illegaloperator");
}
}
@SuppressWarnings("serial")
classIllegalExpressionExceptionextendsRuntimeException
{
publicIllegalExpressionException()
{
}
publicIllegalExpressionException(Stringinfo)
{
super(info);
}
}
多项式加减法与单项式加法:
publicPolyadd(Polyanother)
{
Polyres=newPoly();
res.polyList.addAll(this.polyList);
res.polyList.addAll(another.polyList);
res.sort();
returnres;
}
publicPolysubtract(Polyanother)
{
returnthis.add(another.negate());
}
publicNodeadd(Nodeanother)
{
doubler=this.ratio+another.ratio;
Noderes=newNode(r);
res.exp=newTreeMap(this.exp);
returnres;
}
总结(对所作程序进行分析、评价运行效果,总结遇到的问题和解决办法)
这次的收获很大,明白了老师让我们做这个程序的目的,合作,分工。
在这次程序调试中,我们学会了很多调试技巧,提高了调试的效率,也加快了进度.
另外我们明白了一个好的代码结构体系能够加快编写速度以及调试速度,进一步理解了重构以及面向对象思想的优点和技巧.
首先在我们在做程序之前,经过商讨,我们确定了整个程序的框架,理清了步骤。
我们使用了模块化设计,对程序进行了封装,确定了几个可以实现程序的类,用类之间的合理的调用实现了整个的算法。
用封装成类的方法,也提高了程序的复用性。
在写程序的过程中也遇到了很多的问题
在编写的时候很多变量写错,产生异常,死循环,经过不断的调试才更改好.
我们组里也有过争论,为数据的存储结构产生了争议,但是通过讨论,最终确定了一个合理的方案。
对于多项式的加,减,乘,除取整,除取模运算,其实有很多情况,实现起来有很大难度,比如,系数为分数,幂次为分数并且有正有负,系数为带跟号的数字,幂次带跟号,甚至是无理数等等,情况十分复杂。
但幸运的是,此题只限制在整数范围,所以难度大大降低。
但是,对于输入信息的解析,除取整以及除取模运算,也存在一定难度。
在选取数据结构时,采用了java内部集合Treemap来存储多项式各项系数信息以及幂次信息,免去了自己编写链表的工作,并且,在运算时,也带来了极大的方便。
在编写函数时,尽量使函数的功能单一,使其相对独立,既缩短了每个函数代码长度,也方便了函数的调用。
并且,由于函数长度较短,加之有备注以及说明,因此可读性实在不错啊。
通过此题,不仅锻炼了我们编程能力,同时也锻炼了我们团队协作的能力,收获很大,最后十分感谢老师给予的热情的帮助。