我试图通过分而治之的策略来实现Factorial功能。我使用ForkJoin框架来分叉每个递归任务以加速计算。 但我发现它并没有像我预期的那样加速。 不使用ForkJoin计算50000的阶乘就需要28秒 当我使用ForkJoin时花了25秒。 这是没有forkjoin的代码:
public static BigInteger factorial(long p, long q) {
if (q < p) {
return new BigInteger("1");
}
if (p == q) {
return new BigInteger("" + p);
}
BigInteger fact = new BigInteger("1");
fact = fact.multiply(factorial(p, (p + q) / 2)).multiply(factorial((p + q) / 2 + 1, q));
return fact;
}
这是forkJoin的代码:
public class Factorial extends RecursiveTask<BigInteger>{
private long p, q;
public Factorial(long p, long q) {
this.p = p;
this.q = q;
}
@Override
public BigInteger compute() {
if(q < p) {
return new BigInteger("1");
}
if( p == q) {
return new BigInteger(""+p);
}
Factorial f1 = new Factorial(p, (p+q)/2);
Factorial f2 = new Factorial((p+q)/2 + 1, q);
f2.fork();
return f1.compute().multiply(f2.join());
}
}
我哪里错了?我认为这不是Fork / Join的结果。请帮助!
答案 0 :(得分:4)
Fork / Join可以对计算进行并行化。它是:给你一个恒定的增益(通过例子将时间除以4)。这是由你拥有的真实CPU所限制的(200个线程将仅通过示例共享相同的4个CPU)。
相比之下,阶乘(典型的算法)是O(N!)
,这意味着它的增长速度非常快。
如果你为每一步创建一个新的分叉,分叉和连接的开销会补偿并行化的收益。
所以重要的是尝试使用另一个不是O(N!)
的算法来计算阶乘。如果您应用动态编程(重新使用中间结果),则可以将其转换为O(N)
。
我不知道你试图模仿的算法是我应该做的是保持一个矩阵或者用于计算对的东西,以便在我第二次需要它时重复使用它们......
查看您的代码,我可以看到每个因子方法执行会引发两个子执行:(p+q)/2,q
和p,(p+q)/2+1
......或类似的东西。如果每个时间因子方法都找到结果,将其保存在映射[Pair -> BigDecimal]
中,则可以在方法开头查询此缓存。使此映射成为类的成员(或通过参数传递),以便不同的方法调用共享映射。
factorial(p,q) {
if (there is result for (p,q) in the map)
return it
else
{
normal computation (provokes two child invocations)
save result into cache
}
}