使用多线程的因子

时间:2016-04-02 00:00:21

标签: java multithreading

我在使用多线程解决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

请提供评论代码,请希望得到您的积极回应。谢谢您一直走到最后

4 个答案:

答案 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!

  • 主题1 8x7x6x5
  • 主题2 4x3x2x1

一种方法是在外部方法中分配工作,将范围传递到每个线程,然后将结果相乘。

实际上,您可以使用流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;
}

}