我需要为大学做一个编程任务.任务是实现一个程序,返回方程q²3qpp²=r²(q,p,r素数)的所有解.随后,程序将通过并行化加速.
不幸的是我们必须使用BigInteger,所以不要感到惊讶.
这是我写的课程,它准确计算了这个等式.
public boolean calculateEquation() {
//Equation: p² + 3pq + q² = r²
if (calculatePSquare().add(calculate3TimesPQ()).add(calculateQSquare()).equals(calculateRSquare())) {
System.out.println("p: " + p + " q: " + q + " r: " + r);
}
return calculatePSquare().add(calculate3TimesPQ()).add(calculateQSquare()).equals(calculateRSquare());
}
@Override
public void run() {
calculateEquation();
}
该类的完整代码:
https://pastebin.com/wwrDurUT
我的下一步是测试代码并停止时间以查看并行化是否稍后工作.为了实现并行化,我查看了不同的主题,其中包括链接的主题:What is the easiest way to parallelize a task in java?
这是我的结果:
ExecutorService executorService = Executors.newFixedThreadPool(Configuration.instance.maximumNumberOfCores);
ExecutorCompletionService executorCompletionService = new ExecutorCompletionService(executorService);
for (BigInteger pValue : possiblePValues) {
for (BigInteger qValue : possibleQValues) {
for (BigInteger rValue : possibleRValues) {
executorCompletionService.submit(new Thirteen(pValue, qValue, rValue), null);
}
}
}
executorCompletionService.take();
完整代码:
https://pastebin.com/kviEnnFH
现在有趣的是,如果任务数量很少,并行化版本只会更快.对于0-500之间的所有素数,并行化版本更快.如果我取0到2000之间的所有素数,结果看起来非常不同:
所有素数在0到100之间:
没有并行化:
任务花了:110ms
并行:
任务花了:64ms
0到2000之间的所有素数:
没有并行化:
任务花了:7797ms
并行:
任务花了:25799ms
由于关于这个主题的资源很少有易于理解,而且我真的不太明白我的代码究竟是什么,我很惊讶为什么它的行为如此.
最佳答案 首先,使用三重嵌入式for循环实现这个p²3pqq²=r²方程是没有意义的,这导致具有O(n)= n ^ 3的复杂度.这可以仅使用两个for循环来完成,因为如果我们知道p和q的值,则可以使用等式计算r.
for (BigInteger p: possiblePValues) {
for (BigInteger q: possibleQValues) {
BigInteger r = p.pow(2).add(q.pow(2)).add(p.multiply(q).multiply(new BigInteger("3")))
// decide if sqrt(r) is prime
}
}
现在,如果我们已经在Hashmap中保存了预先计算的素数,我们可以通过查找立即确定r是否为素数.
关于parallalization:
您的方法是错误的,因为您只是为某些操作生成线程,这些操作需要相对较短的时间才能完成.可能线程管理的开销比计算方程本身的操作花费的时间更长.我猜这就是为什么你的多线程版本运行时间更长的原因.
关于如何并行化两个嵌入式for循环没有黄金法则.我的方法是将第一个循环分成较小的块,具体取决于你拥有多少个核心并创建一些间隔.例如,如果我们有100个素数和4个线程,第一个线程将采用索引从0到24的p值,第二个线程从25到49,依此类推.第二个for循环应该在每个线程中从0到100运行.使用此方法,您可以计算所有可能的r值.