递归算法在一定深度后停止工作

时间:2018-05-20 11:34:40

标签: java math recursion integer biginteger

当我发现我的顺序递归算法在某一点断开时,我正在探索Fork / Join框架及其通过因子计数可能带来的速度优势。确切地说,当我尝试计算46342!RecursiveCounter的结果是错误的,但在该值之前,它始终是正确的,并且与ParallelCounter和{{1}的结果相同}。有谁知道为什么会发生这种情况?

以下是课程:

RecursiveCounter:

LoopCounter

循环计数器:

public class RecursiveCounter implements FactorialCounter, RangeFactorialCounter {
    @Override
    public BigInteger count(int number) {
        return count(1, number);
    }

    @Override
    public BigInteger count(int from, int to) {
        int middle = (from + to) >> 1;
        BigInteger left;
        BigInteger right;
        if (middle - from > 1)
            left = count(from, middle);
        else
            left = new BigInteger(String.valueOf(from * middle));
        if (to - (middle + 1) > 1)
            right = count(middle + 1, to);
        else
            right = to == middle + 1 ? new BigInteger(String.valueOf(to)) : new BigInteger(String.valueOf((middle + 1) * to));
        return left.multiply(right);
    }
}

ParallelCounter的RecursiveTask:

public class LoopCounter implements FactorialCounter, RangeFactorialCounter {
    @Override
    public BigInteger count(final int number) {
        return count(1, number);
    }

    @Override
    public BigInteger count(final int from, final int to) {
        BigInteger result = new BigInteger("1");
        for (int i = from; i < to + 1; i++) {
            result = result.multiply(new BigInteger(String.valueOf(i)));
        }
        return result;
    }
}

2 个答案:

答案 0 :(得分:5)

RecursiveCounterfrom * middle(middle + 1) * to可能会溢出,您需要使用BigInteger来操纵它们:

...
left = BigInteger.valueOf(from).multiply(BigInteger.valueOf(middle));
...
right = to == middle + 1 ? BigInteger.valueOf(to) : BigInteger.valueOf(to).multiply(BigInteger.valueOf(middle + 1));

然后,您可以在RecursiveCounterLoopCounter中获得相同的结果:

LoopCounter loopCounter = new LoopCounter();
RecursiveCounter recursiveCounter = new RecursiveCounter();
BigInteger loopResult = loopCounter.count(46342);
BigInteger recursiveResult = recursiveCounter.count(46342);
System.out.println(loopResult.equals(recursiveResult)); // true

答案 1 :(得分:4)

这是因为int的数字溢出,而不是因为递归深度,这是由您的算法很好地控制,需要O(log 2 n)堆栈帧进行递归

溢出发生在这里:

new BigInteger(String.valueOf((middle + 1) * to))

to为高时,此值可能会溢出int。具体来说,当middle在第二个&#34; leg&#34;中接近to时对于递归调用,您将46341乘以46342,由于溢出(demo)而产生-2147432674

您可以通过仅使用BigInteger来解决此问题&#34;有效负载&#34;乘法,即

BigInteger.valueOf(middle+1).multiply(BigInteger.valueOf(to))