Java ExecutorService解决递归Fibonacci系列

时间:2011-07-02 04:47:07

标签: java multithreading recursion fibonacci executorservice

我需要使用线程以递归方式找出Fibonacci系列中某些索引的数字,我尝试了以下代码,但程序永远不会结束。如果我遗失了什么,请告诉我。

代码

  import java.math.BigInteger;
  import java.util.concurrent.*;

  public class MultiThreadedFib {

    private ExecutorService executorService;

    public MultiThreadedFib(final int numberOfThreads) {
      executorService = Executors.newFixedThreadPool(numberOfThreads);
    }

    public BigInteger getFibNumberAtIndex(final int index) 
      throws InterruptedException, ExecutionException {

      Future<BigInteger> indexMinusOne = executorService.submit(
        new Callable<BigInteger>() {
          public BigInteger call() 
          throws InterruptedException, ExecutionException {
            return getNumber(index - 1);
          }
      });

      Future<BigInteger> indexMinusTwo = executorService.submit(
        new Callable<BigInteger>() {
          public BigInteger call() 
          throws InterruptedException, ExecutionException {
            return getNumber(index - 2);
          }
      });

      return indexMinusOne.get().add(indexMinusTwo.get());
    }

    public BigInteger getNumber(final int index) 
    throws InterruptedException, ExecutionException {
      if (index == 0 || index == 1)
        return BigInteger.valueOf(index);

      return getFibNumberAtIndex(index - 1).add(getFibNumberAtIndex(index - 2));
    }
  }

修正了(感谢 fiver

我没有从call方法中调用getNumber(int),而是调用一个动态编程算法来计算该索引处的数字。

代码是:

public class DynamicFib implements IFib {

private Map<Integer, BigInteger> memoize = new HashMap<Integer, BigInteger>();

public DynamicFib() {
  memoize.put(0, BigInteger.ZERO);
  memoize.put(1, BigInteger.ONE);
}

public BigInteger getFibNumberAtIndex(final int index) {

  if (!memoize.containsKey(index))
    memoize.put(index, getFibNumberAtIndex(index - 1).add(getFibNumberAtIndex(index - 2)));

  return memoize.get(index);
  }
}

3 个答案:

答案 0 :(得分:1)

这种递归会非常快地溢出堆栈。这是因为你反复计算较低的斐波那契数 - 指数多次。

避免这种情况的一种有效方法是使用memoized recursion(动态编程方法)

基本上使用静态数组来保存已经计算过的斐波纳契数,无论何时需要,都可以从数组中取出,如果已经计算过的话。如果没有,则计算并将其存储在数组中。这样每个数字只会计算一次。

(您可以使用其他数据结构而不是数组,当然,也就是哈希表)

答案 1 :(得分:1)

您正在做的是通过线程/任务使用递归替换简单递归。

在你看到fib(0)和fib(1)的情况之前,每个任务都会再提交两个任务,然后等待它们完成。在等待时,它仍在使用线程。由于线程池是有界的,你很快就会调用submit来阻止...并且整个计算都会锁定。


除此之外,你在indexMinusTwo中遇到了一个错误,导致计算错误答案。


  

但是,递归多线程程序还需要比memoized递归非多线程程序更长的时间。任何提高性能的技巧?

即使假设您“修复”了上述问题(例如,通过使用无界线程池),无法您将能够执行更好性能的多线程斐波那契版本而不是使用memoization的单线程版本。计算根本不适合并行化。

答案 2 :(得分:0)

当您执行独立任务时,线程最有效。根据定义,斐波那契系列没有任何程度的并行性。每个f(n)取决于前两个值。因此,使用多个线程比使用多个线程更快地计算f(n)(除非你的算法效率低)

对于大数字,你唯一可以并行的+操作是平行的,但这可能是a)复杂的b)难以比单线程解决方案更快。

计算斐波纳契数的最快/最简单的方法是在一个线程中使用循环。您不需要使用recusrion或记忆值。