选择显示字体大小

利用java动态编译计算数学表达式

前几天要做一个计算数学表达式的题目,本来计划使用解析表达式的方法来解析各种数学表达式,然后再动态计算表达式的值.后来考虑到这样编程的任务很重,时间有限 后来在网上搜搜,看到使用动态编译并使用反射机制 ,这样计算表达式的编程就容易多了.下面是我这次编程的例子, 请大家看看.

01 /*
02  * created on 2006-3-8
03  * @author icerain 我的blog: http://blog.matrix.org.cn/page/icess
04  */
05
06 public interface ioperator {
07   string sin = "sin";
08   string cos = "cos";
09   string tan = "tan";
10   string asin = "asin";
11   string acos = "acos";
12   string atan = "atan";
13   string exp = "exp";
14   string log = "log";
15   string pow = "pow";
16   string sqrt = "sqrt";
17   string fabs = "fabs";
18   string minus = "minus";
19  
20   string j_sin = "math.sin";
21   string j_cos = "math.cos";
22   string j_tan = "math.tan";
23   string j_asin = "math.asin";
24   string j_acos = "math.acos";
25   string j_atan = "math.atan";
26   string j_exp = "math.exp";
27   string j_log = "math.log10";
28   string j_pow = "math.pow";
29   string j_sqrt = "math.sqrt";
30   string j_fabs = "math.abs";
31  
32 }

定义一个接口, 用来转换各种数学符号为java类库中的表达式.

下面是用来计算的代码.

001 /*
002  * created on 2006-3-7
003  * @author icerain 我的blog: http://blog.matrix.org.cn/page/icess
004  */
005 //package hust.icess.simpson;
006
007
008 import java.util.logging.level;
009
010 import java.io.*;
011 import java.lang.reflect.method;
012 import java.util.scanner;
013 import java.util.logging.logger;
014
015
016 import com.sun.tools.javac.*;
017 /**
018  * 利用simpson公式计算积分,在输入被积公式时候请注意使用如下格式.
019  * 1.只使用圆括号() , 没有别的括号可以使用.如: 1/(1+sin(x))
020  * 2.在输入超越函数的时候,变量和数值用括号扩起来 如:sin(x) 而不要写为 sinx
021  * 3.在两个数或者变量相乘时候,不要省略乘号* 如:2*a 不要写为 2a
022  * 4.在写幂运算的时候,请使用如下格式:
023  * 利用动态编译来计算simpson积分,使用该方法 编程相对简单,运行效率有点慢.
024  * @author icerain
025  *
026  */
027 public class simpson implements ioperator {
028   /**
029    * logger for this class
030    */
031   private static final logger logger = logger.getlogger(simpson.class
032       .getname());
033
034   private string expression = null;
035
036   private string variable = null;
037
038   private string[] variablevalue = new string[3];
039
040   // private static main javac = new main();
041
042   /**主函数 */
043   public static void main(string[] args) throws exception {
044     simpson sim = new simpson();
045     system.out.println("结果如下:");
046     system.out.print(sim.getsimpsonvalue());
047     system.exit(0);
048
049   }
050
051   public simpson() {
052     logger.setlevel(level.warning);
053     init();
054   }
055
056   /** 初始化用户输入,为技术simpson积分做准备. */
057   private void init() {
058     scanner scanner = new scanner(system.in);
059     system.out.println("请输入函数表达式 如 1+sin(a) + cos(a)/a :");
060     // string input = scanner.nextline();
061     //读入被积函数的表达式
062     expression = scanner.nextline().trim().tolowercase();
063     system.out.println("请输入变量字符 如 a :");
064     //读入变量字符
065     variable = scanner.nextline().trim().tolowercase();
066    
067     //处理多元函数 目前不实现该功能
068     // string[] tempvars = tempvar.split(" ");
069     // for(int i = 0; i < tempvars.length; i ++) {
070     // variable[i] = tempvars[i];
071     // }
072
073     system.out.println(&quot;请输入积分区间和结点数 如 2 5.4 10 :&quot;);
074     //读取复合simpson公式的积分参数
075     string tempvalue = scanner.nextline().trim();
076     string[] tempvalues = tempvalue.split(&quot; &quot;);
077     for (int i = 0; i < tempvalues.length; i++) {
078       variablevalue[i] = tempvalues[i];
079     }
080
081   }
082
083   /** 计算 simpson积分的值*/
084   public double getsimpsonvalue() {
085     //保存中间结果
086     double value1 = 0;
087     double value2 = 0;
088     double tempvalue = 0;
089     int i = 0;
090     // 解析输入的积分参数值
091     int n = integer.parseint(variablevalue[2]);
092     double a = double.parsedouble(variablevalue[0]);
093     double b = double.parsedouble(variablevalue[1]);
094     double h = (b - a) / n;
095     //计算value1
096     for (i = 0; i < n; i++) {
097       tempvalue = a + (i + 0.5) * h;
098       string code = getsourcecode(expression, getvariable(), double
099           .tostring(tempvalue));
100       try {
101         value1 += run(compile(code));
102       } catch (exception e) {
103         // todo auto-generated catch block
104         e.printstacktrace();
105
106         if (logger.isloggable(level.info)) {
107           logger.info(&quot;something is wrong&quot;);
108         }
109       }
110     }
111     //计算value2
112     for (i = 1; i < n; i++) {
113       tempvalue = a + i * h;
114       string code = getsourcecode(expression, getvariable(), double
115           .tostring(tempvalue));
116       try {
117         value2 += run(compile(code));
118       } catch (exception e) {
119         // todo auto-generated catch block
120         e.printstacktrace();
121         if (logger.isloggable(level.info)) {
122           logger.info(&quot;something is wrong&quot;);
123         }
124       }
125     }
126
127     //计算f(a) f(b) 的函数值
128     double valuea = getfunctionvalue(a);
129     double valueb = getfunctionvalue(b);
130     //计算simpson公式的值
131     double resultvalue = (valuea + valueb + 4 * value1 + 2 * value2) * h / 6;
132    
133     return resultvalue;
134   }
135
136   //计算f(a) 的值
137   private double getfunctionvalue(double varvalue) {
138     string code = getsourcecode(expression, getvariable(), double
139         .tostring(varvalue));
140     double result = 0;
141     try {
142       result = run(compile(code));
143     } catch (exception e) {
144       // todo auto-generated catch block
145       e.printstacktrace();
146       if (logger.isloggable(level.info)) {
147         logger.info(&quot;something is wrong&quot;);
148       }
149     }
150     return result;
151   }
152
153   /**
154    * 得到用户输入表达式转换为java中的可计算表达式的函数
155    * @param ex 输入的表达式 如: 1/(1 + sin(x))
156    * @param var 表达式中的变量 如: x
157    * @param value 变量的取值 如: 4.3
158    * @return java中可以直接计算的表达式 如: 1/(1 + math.sin(x))
159    */
160   private string getsourcecode(string ex, string var, string value) {
161     string expression = ex;
162     //计算多个变量的函数的时候使用
163    
164     expression = expression.replaceall(var, value);
165    
166     //处理数学符号
167     if (expression.contains(sin)) {
168       expression = expression.replaceall(sin, j_sin);
169     } else if (expression.contains(cos)) {
170       expression = expression.replaceall(cos, j_cos);
171     } else if (expression.contains(tan)) {
172       expression = expression.replaceall(tan, j_tan);
173     } else if (expression.contains(asin)) {
174       expression = expression.replaceall(asin, j_asin);
175     } else if (expression.contains(acos)) {
176       expression = expression.replaceall(acos, j_acos);
177     } else if (expression.contains(atan)) {
178       expression = expression.replaceall(atan, j_atan);
179     } else if (expression.contains(exp)) {
180       expression = expression.replaceall(exp, j_exp);
181     } else if (expression.contains(log)) {
182       expression = expression.replaceall(log, j_log);
183     } else if (expression.contains(pow)) {
184       expression = expression.replaceall(pow, j_pow);
185     } else if (expression.contains(sqrt)) {
186       expression = expression.replaceall(sqrt, j_sqrt);
187     } else if (expression.contains(fabs)) {
188       expression = expression.replaceall(fabs, j_fabs);
189     }
190
191     return expression;
192   }
193
194   /** 编译javacode,返回java文件*/
195   private synchronized file compile(string code) throws exception {
196     file file;
197     // 创建一个临时java源文件
198     file = file.createtempfile(&quot;javaruntime&quot;, &quot;.java&quot;, new file(system
199         .getproperty(&quot;user.dir&quot;)));
200     if (logger.isloggable(level.info)) {
201       logger.info(system.getproperty(&quot;user.dir&quot;));
202     }
203     // 当jvm 退出时 删除该文件
204      file.deleteonexit();
205     // 得到文件名和类名
206     string filename = file.getname();
207     if (logger.isloggable(level.info)) {
208       logger.info(&quot;filename: &quot; + filename);
209     }
210     string classname = getclassname(filename);
211     // 将代码输出到源代码文件中
212     printwriter out = new printwriter(new fileoutputstream(file));
213     // 动态构造一个类,用于计算
214     out.write(&quot;public class &quot; + classname + &quot;{&quot;
215         + &quot;public static double main1(string[] args)&quot; + &quot;{&quot;);
216     out.write(&quot;double result = &quot; + code + &quot;;&quot;);
217     //用于调试
218     //out.write(&quot;system.out.println(result);&quot;);
219     out.write(&quot;return new double(result);&quot;);
220     out.write(&quot;}}&quot;);
221     //关闭文件流
222     out.flush();
223     out.close();
224     //设置编译参数
225     string[] args = new string[] { &quot;-d&quot;, system.getproperty(&quot;user.dir&quot;),
226         filename };
227     //调试
228     if (logger.isloggable(level.info)) {
229       logger.info(&quot;编译参数: &quot; + args[0]);
230     }
231     //process process = runtime.getruntime().exec(&quot;javac &quot; + filename);
232     int status = main.compile(args);
233     //输出运行的状态码.
234     //    状态参数与对应值
235     //      exit_ok 0
236     //      exit_error 1
237     //      exit_cmderr 2
238     //      exit_syserr 3
239     //      exit_abnormal 4
240     if (logger.isloggable(level.info)) {
241       logger.info(&quot;compile status: &quot; + status);
242     }
243     //system.out.println(process.getoutputstream().tostring());
244     return file;
245   }
246
247   /**
248    * 运行程序 如果出现exception 则不做处理 抛出!
249    * @param file 运行的文件名
250    * @return 得到的simpson积分公式的结果
251    * @throws exception 抛出exception 不作处理
252    */
253   private synchronized double run(file file) throws exception {
254     string filename = file.getname();
255     string classname = getclassname(filename);
256     double tempresult = null;
257     // system.out.println(&quot;class name: &quot; +classname);
258     //当jvm 退出时候 删除生成的临时文件
259     new file(file.getparent(), classname + &quot;.class&quot;).deleteonexit();
260     try {
261       class cls = class.forname(classname);
262       //system.out.println(&quot;run........&quot;);
263       // 映射main1方法
264       method calculate = cls
265           .getmethod(&quot;main1&quot;, new class[] { string[].class });
266       //执行计算方法 得到计算的结果
267       tempresult = (double) calculate.invoke(null,
268           new object[] { new string[0] });
269     } catch (securityexception se) {
270       system.out.println(&quot;something is wrong !!!!&quot;);
271       system.out.println(&quot;请重新运行一遍&quot;);
272     }
273     //返回值
274     return tempresult.doublevalue();
275   }
276
277   /** 调试函数*/
278   // private void debug(string msg) {
279   // system.err.println(msg);
280   // }
281
282   /** 得到类的名字 */
283   private string getclassname(string filename) {
284     return filename.substring(0, filename.length() - 5);
285   }
286
287  
288   //getter and setter
289   public string getexpression() {
290     return expression;
291   }
292
293   public void setexpression(string expression) {
294     this.expression = expression;
295   }
296
297   public string getvariable() {
298     return variable;
299   }
300
301   public void setvariable(string variable) {
302     this.variable = variable;
303   }
304
305   public string[] getvariablevalue() {
306     return variablevalue;
307   }
308
309   public void setvariablevalue(string[] variablevalue) {
310     this.variablevalue = variablevalue;
311   }
312 }

这样就可以用来计算了.

下面编写一个.bat文件来运行改程序.(在这里没有打包为.jar文件)

@echo 注意:
@echo ***********************************************************
@echo * 利用simpson公式计算积分,在输入被积公式时候请注意使用 ***
@echo * 如下格式. ***
@echo * 1.只使用圆括号() , 没有别的括号可以使用.如: ***
@echo * 1/(1+sin(x)) ***
@echo * 2.在输入超越函数的时候,变量和数值用括号扩起来 如: ***
@echo * sin(x) 而不要写为 sinx ***
@echo * 3.在两个数或者变量相乘时候,不要省略乘号* 如: ***
@echo * 2*a 不要写为 2a ***
@echo * 4.在写幂运算的时候,请使用如下格式: ***
@echo * pow(x,y) 代表x的y次幂 不要使用其他符号 ***
@echo * 5.绝对值请用如下符号表示: ***
@echo * fabs(x) 代表x的绝对值 ***
@echo * 6.指数函数请用exp表示 如:exp(x) ***
@echo * 7.对数函数请用log(x)表示, 该处对数是指底为10的对数, ***
@echo * 计算不是以10为底的对数时候请转换为10为底的对数 ***
@echo * 8.变量字符请不要与函数中的其他字符重合,如 如果使用了 ***
@echo * sin 函数请 不要用 s i 或者n做为变量,否则在解析 ***
@echo * 表达式时候 会出错 ^_^
@echo ***********************************************************

@rem 在编译源文件时候 要使用下面的命令 把rem 删除即可 注意 由于文件中用到了tools.jar中
@rem 的命令 所有在编译的时候 用适当的classpath 替换下面的 tools.jar的路径 运行的时候一样

@rem javac -classpath &quot;.;d:\program files\java\jdk1.5.0_03\lib\tools.jar;%classpath%&quot; simpson.java %1

@rem 注意更改此处的tools.jar的路径 为你当前系统的正确路径
@java -cp &quot;.;d:\program files\java\jdk1.5.0_03\lib\tools.jar&quot; simpson

@pause

 

这样就可以了.

说明:

使用该方法来计算本程序,由于要多次动态产生计算源代码,并且编译 在性能上会有很大损失. 要是在项目中不经常计算表达式 使用该方法可以减轻编程的负担.要是象上面那样 要多次计算的话,使用该方法是很值得考虑的.



 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons