我在使用多线程解决Factorial的线程上遇到了一个噩梦我有一个简单的想法,比如为什么我们不使用quicksort技术来解决这个问题我在谈论分而治之......
例如我在代码格式中的解释......
假设如果我找到8的阶乘,让我们将一个分数限制在2个部分
thread1求解阶乘n到n / 2
8*fact(7)
8*7*fact(6)
8*7*6*fact(5)
8*7*6*5*fact(4)
thread2计算fact(4)
现在我的问题我不知道如何解决如何从thread1中删除fact(4)以便我完成我的答案
result=thread1output*thread2output
请提供评论代码,请希望得到您的积极回应。谢谢您一直走到最后
答案 0 :(得分:1)
当一个计算取决于先前计算的结果时,通常适用的优化是{{3}}。而不是简单地递归,检查是否已经计算了值;如果是,则返回先前计算的值。像,
private static Map<Integer, BigInteger> memo = new HashMap<>();
static {
memo.put(1, BigInteger.ONE);
}
public static BigInteger fact(int i) {
if (memo.containsKey(i)) {
return memo.get(i);
}
BigInteger result = BigInteger.valueOf(i).multiply(fact(i - 1));
memo.put(i, result);
return result;
}
答案 1 :(得分:1)
如果要使用多个线程攻击factorial,则需要将系列划分为工作单元。使用2个线程,一种简单的方法是将系列划分为2。
例如8!
一种方法是在外部方法中分配工作,将范围传递到每个线程,然后将结果相乘。
实际上,您可以使用流API执行此操作:
long factorial = LongStream.rangeClosed(1, n).parallel().reduce((a, b) -> a * b).get();
这可以并行处理计算。
对于大于20的n,结果将超过long
可以容纳的最大值;使用BigInteger
代替算术:
BigInteger factorial = IntStream.rangeClosed(1, n)
.parallel()
.mapToObj(String::valueOf)
.map(BigInteger::new)
.reduce((a, b) -> a.multiply(b))
.get();
答案 2 :(得分:1)
Elliott的评论是正确的,多线程并不是优化因子计算的好方法。当然,在计算真正大的因子之前,它不会有效(例如与记忆相比)。
在简单的递归分而治之策略中,伪代码是
factorial(N) =
return factorialHelper(set_of(1, N))
factorialHelper(set) =
if set.size == 1
return set.first
else
let set1, set2 = split_into_subsets(set, 2)
return factorialHelper(set1) * factorialHelper(set2)
现在我们可以天真地使用这样的线程:
factorialHelper(set) =
if set.size == 1
return set.first
else
let set1, set2 = split_into_subsets(set, 2)
return fork(lambda factorialHelper(set1)).join() *
fork(lambda factorialHelper(set2)).join()
(解释:fork(lambda factorialHelper(set1)).join()
应该意味着我们是:
- 创建一个新线程来计算factorialHelper(set1)
,
- 开始线程,
- 等待它传递结果。)
这就是问题所在。每个线程都在做一堆内务处理(例如拆分集合),然后进行单次乘法。如果你看一下大局,那就意味着N!需要大约N个线程。
我们可以做得更好。而不是等待两个子线程完成。 &#34;当前&#34;线程可以完成其中一个的工作; e.g。
factorialHelper(set) =
if set.size == 1
return set.first
else
let set1, set2 = split_into_subsets(set, 2)
let child = fork(lanbda factorialHelper(set1))
return factorialHelper(set2) * child.join()
但是仍然需要大约N / 2个线程。还有更大的问题:
因此,如果你用Java编写上面的代码,它会像狗一样运行。线程太多了。太多的线程创建/切换开销和每个线程的有用的工作太少。
如果您使用Java Fork / Join Pool,您可以做得更好,但您还需要对split_into_subsets(set, 2)
步骤采取一些措施。这将会做很多多余的工作复制设置元素。
经典的方法是拥有一个共享数组(1到N)并通过&#34; low&#34;和&#34;高&#34;索引。但在这种情况下,我们甚至不需要数组,因为我们知道array[i]
与i
相同。 (忽略一个。我还在说伪代码!)
但后来我们遇到了最后的问题。平衡工作量。将两个大数(例如BigInteger
)数相乘的工作不是常数。它实际上取决于数字的大小。 (我认为对于M * N
它是O(log 2 M
* log 2 N
)。如果你使用天真的鸿沟而且,征服,&#34;左端&#34;的乘法比#&#34;右端&#34;快得多。解决这个问题很棘手,我怀疑分而治之是处理错误的方法用它。
我实际上会考虑采用混合方法:
T
个工作线程,其中T
对应于可用核心数。1
通过N
分发到T
个子集。在计算结束时,无论你如何操作,都会有一个最后的乘法,使用一个线程完成。之前的两个计算可以通过最多2个线程来完成。所以最后,一些线索&#34;饥饿&#34;将是不可避免的。
最后,还有乘法本身。显然,如果我们为多个线程计算阶乘(N)足够大的N值,那么结果将大于Long.MAX_VALUE。使用BigInteger
将是一个明显的选择。但是,BigInteger.multiply(BigInteger)
上有一个问号。考虑高性能N位乘法算法和可并行化的乘法算法可能是值得的。从这里开始:
对于记录,两个n位数字的BigInteger.multiply(BigInteger)
的复杂性如下:
Java 6使用&#34;男生&#34;算法;即n位数的复杂度为O(n * n)
Java 8使用&#34;男生&#34;算法,Karatsuba算法(O(n * 1.585))或3路Toom-Cook乘法(O(n * 1.465))取决于n的大小。所以最终,复杂性是O(n * 1.465)。
答案 3 :(得分:0)
这段代码怎么样: - 1.获取输入编号并创建对(开始,结束)。 2.创建一个乘以给定数字并返回结果的任务; 3.提交任务。 4.使用future获得结果。
公共类TestFactorial {
public static void main(String[] args) throws InterruptedException,ExecutionException {
Scanner scanner = new Scanner(new InputStreamReader(System.in));
System.out.println(" Please enter the number for which you want to calculate Factorial");
String input = scanner.nextLine();
int number =0;
int answer =1;
scanner.close();
if(input!=null)
number = Integer.valueOf(input);
if(number==0 || number ==1){
answer = 1;
}
calcuateFactorial(number, answer);
}
private static void calcuateFactorial(int number, int answer) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<Integer>> totalTaskResults = new ArrayList<Future<Integer>>();
for(int start =1;start<=number ;start++){
int end = start+1;
Future<Integer> taskResult;
if(end>start)
taskResult = executor.submit(new Multiply(start, 1));
else
taskResult= executor.submit(new Multiply (start, end));
totalTaskResults.add(taskResult);
}
for (Future<Integer> future :totalTaskResults){
answer = answer* future.get();
}
executor.shutdown();
System.out.println("Answer "+ answer);
}
}
这是可调用的类(任务): -
public class Multiply implements Callable<Integer>{
private int start ;
private int end;
public Multiply (int start, int end){
this.start= start;
this.end = end;
}
@Override
public Multiply call() throws Exception {
return start*end;
}
}