读书人

大数据的情况下怎么高效的计算表达式的

发布时间: 2011-11-06 07:07:53 作者: rapoo

大数据的情况下如何高效的计算表达式的值
最近的一个项目需要进行蒙特卡洛模拟,并且需要对模拟结果进行表达式求职计算
计算的公式不固定一般的格式是这样$0+$1*2/$3*2.5且随着业务的复杂还需要
进行多次迭代计算。数据量很大最少的也要进行2-3万次计算。

我试了用自己写的表达式求职程序和Mozilla的Rhino效率上都不是很理想,可能是自己
水平比较差,用Mozilla的Rhino居然比自己写的程序快一倍。我们用的jdk1.5所以不能
用jdk1.6中才支持的编译后执行脚本的方法。

想过尝试动态生成Java文件然后编译执行,但是一想起在服务器环境这个程序可能需要
同时计算多个表达式......越想越觉得不可靠.

大家处理这样的问题有什么好的思路,请给些指点.

[解决办法]
蒙特卡洛算法我们也用。。。可惜是数学家算好给我们的。。。。

帮顶吧
[解决办法]
我不知道 BeanShell 的速度如何?
[解决办法]
路过 学习

帮顶下
[解决办法]
以脚本语言的计算能力根本就不适合做大规模数值计算。
你可以先把一个固定的计算公式用java写好,然后跟Rhino比较一下。保守的估计至少要差几十倍以上,rhino的脚本编译运行也不能提高太多的效率,因为它依旧要依靠脚本运行引擎。

如果计算公式不是经常变化的话,还是可以用java写,用javac动态编译。同时可以考虑用文件保存编译过的class。


[解决办法]
不懂,顶下
[解决办法]
5000 个数据计算 $0+$1*$2+$3 使用 BeanShell 的话需要 17~18 秒的时间,好像太耗时间了。
[解决办法]
哈哈,改进了一下,把循环放到 BeanShell 里面用 JVM 去执行,5000 个数据计算 $0+$1*$2+$3 使用 BeanShell 的话压缩到了不到 600ms

参考:

Java code
import java.util.Random;import bsh.EvalError;import bsh.Interpreter;public class Test2 {        public static void main(String[] args) throws EvalError {                final int count = 5000;        final int paramCount = 4;                double[][] datas = new double[paramCount][count];        Random ran = new Random();        for(int i = 0; i < datas.length; i++) {            for(int j = 0; j < datas[i].length; j++) {                datas[i][j] = ran.nextDouble() * count;            }        }        double[] results = new double[count];                long t0, t1;        t0 = System.currentTimeMillis();                String exp = "$0 + $1 * $2 + $3";        eval(exp, results, datas);                t1 = System.currentTimeMillis();        System.out.println(t1 - t0);                // 测试正确性        double[] res = new double[count];        for(int j = 0; j < datas[0].length; j++) {           for(int i = 0; i < datas.length; i++) {               res[j] = datas[0][j] + datas[1][j] * datas[2][j] + datas[3][j];            }        }                for(int i = 0; i < count; i++) {            if(results[i] != res[i]) {                System.out.printf("error! %.6f --> %.6f%n", results[i], res[i]);            }        }    }        public static void eval(String expression, double[] results, double[]... datas) throws EvalError {                String ep = expression.replaceAll("\\$(\\d+)", "datas[$1][j]");                String exp =            " for(int j = 0; j < datas[0].length; j++) {" +            "   for(int i = 0; i < datas.length; i++) {" +            "     results[j] = " + ep + ";" +            "   } " +            " }";        Interpreter bsh = new Interpreter();        bsh.set("datas", datas);        bsh.set("results", results);        bsh.eval(exp);        results = (double[])bsh.get("results");    }}
[解决办法]
xuexi
[解决办法]
BeanShell 支持的是 Java 语言
[解决办法]
哈哈,我终于知道前面为什么要 600ms 了,因为我多做了几次循环,哎,真粗心!



改正后实际的工作时间在 150ms 左右(由于 JDK 的初始化问题,测试三次使用平均数)。有两种实现方式:

第一种方法:

Java code
import java.util.Random; 

import bsh.EvalError;
import bsh.Interpreter;

public class Test2 {

public static void main(String[] args) throws EvalError {
final int count = 5000;
final int paramCount = 4;

double[][] datas = new double[paramCount][count];
random(datas);
double[] results = new double[count];

long t0, t1;
String exp = "$0 + $1 * $2 + $3";
t0 = System.currentTimeMillis();
eval(exp, results, datas);
t1 = System.currentTimeMillis();
System.out.println(t1 - t0);
testCorrect(results, datas);

random(datas);
t0 = System.currentTimeMillis();
eval(exp, results, datas);
t1 = System.currentTimeMillis();
System.out.println(t1 - t0);
testCorrect(results, datas);

random(datas);
t0 = System.currentTimeMillis();
eval(exp, results, datas);
t1 = System.currentTimeMillis();
System.out.println(t1 - t0);
testCorrect(results, datas);
}

public static void eval(String expression, double[] results, double[]... datas) throws EvalError {

String ep = expression.replaceAll("\\$(\\d+)", "datas[$1][j]");

String exp =
"for(int j = 0, k = datas[0].length; j < k; j++) {" +
" results[j] = " + ep + ";" +
"}";
Interpreter bsh = new Interpreter();
bsh.set("datas", datas);
bsh.set("results", results);
bsh.eval(exp);
results = (double[])bsh.get("results");
}

/**
* 随机生成数据值
*
* @param datas
*/
private static void random(double[][] datas) {
Random ran = new Random();
for(int i = 0; i < datas.length; i++) {
for(int j = 0; j < datas[i].length; j++) {
datas[i][j] = ran.nextDouble() * datas[i].length;
}
}
}

/**
* 测试调用的结果是否正确
* @param results
* @param datas
*/
private static void testCorrect(double[] results, double[][] datas) {
double[] res = new double[results.length];
for(int j = 0; j < datas[0].length; j++) {
for(int i = 0; i < datas.length; i++) {
res[j] = datas[0][j] + datas[1][j] * datas[2][j] + datas[3][j];
}
}
for(int i = 0; i < results.length; i++) {
if(results[i] != res[i]) {
System.out.printf("error! %.6f --> %.6f%n", results[i], res[i]);
}
}
}
}


第二种方法,只是更改 eval 方法,使用字符串更像一个 Java 方法:

Java code
    public static void eval(String expression, double[] results, double[]... datas) throws EvalError {        String ep = expression.replaceAll("\\$(\\d+)", "datas[$1][j]");                String exp =            "public void eval(double[] results, double[][] datas) {" +            "  for(int j = 0, k = datas[0].length; j < k; j++) {" +            "    results[j] = " + ep + ";" +            "  }" +            "}";        Interpreter interpreter = new Interpreter();        NameSpace ns = NameSpace.JAVACODE;        interpreter.eval(exp, ns);        BshMethod method = ns.getMethods()[0];        method.invoke(new Object[]{ results, datas }, interpreter);            } 


[解决办法]
不知道楼主的问题急不急。
我也不太懂 表达式求值、编译原理 之类的知识,说白了,我就是一小白。

如果楼主有空的话,并且,问题仍没解决的话。
可以看看我的以前写的一点代码,记得是关于表达式求值的。
楼主运行一下,看看效率能否满足您的要求。
地址是:http://blog.csdn.net/preferme/archive/2009/07/20/4365251.aspx
[解决办法]
占上!以后看!
[解决办法]

探讨
谢谢18#楼的哥们,这种表达式求值的程序肯定是不能满足需求的。
因为即使你的效率再高效,连续执行5000次就要执行5000次调用,
就要完成5000次解析,所以根本不可行,除非你自己写的解析程序
能够模拟循环。所以对于这样大数据量的计算必须解决如何避免多次
解析的问题。而解决办法就是要么用脚本,要么实现Java代码的动态
编译动态执行。我找到的那个类库比较理想,可以实现将字符串编译
为类然后加载使用

[解决办法]
强烈要求斑竹设置此帖子为精华帖。

读书人网 >J2SE开发

热点推荐