Recursive algorithm stops working after certain depth

Leonid Bor :

I was exploring Fork/Join framework and its possible speed benefits through factorial counting, when discovered that my sequential recursive algorithm breaks at a certain point. To be precise, when I try to count 46342! the result from RecursiveCounter is wrong, but before that value it is always right and is the same that result from ParallelCounter and LoopCounter. Does anyone have an idea why that may happen?

Here are the classes:

RecursiveCounter:

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);
    }
}

LoopCounter:

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;
    }
}

A RecursiveTask for ParallelCounter:

public class FactorialTask extends RecursiveTask<BigInteger> {
    private static final int THRESHOLD = 1000;
    private RangeFactorialCounter iterativeCounter = new LoopCounter();

    private Integer firstVal;
    private Integer lastVal;

    public FactorialTask(Integer from, Integer to) {
        super();
        this.firstVal = from;
        this.lastVal = to;
    }

    @Override
    protected BigInteger compute() {
        return count(firstVal, lastVal);
    }

    private BigInteger count(int from, int to) {
        int middle = (from + to) >> 1;
        if (to - from > THRESHOLD) {
            List<FactorialTask> tasks = Arrays.asList(new FactorialTask(from, middle), new FactorialTask(middle + 1, to));
            tasks.forEach(RecursiveTask::fork);
            return tasks.stream()
                    .map(RecursiveTask::join)
                    .map(BigInteger.class::cast)
                    .reduce(new BigInteger("1"), BigInteger::multiply);
        } else {
            return (from != to) ? countSequential(from, to) : new BigInteger(String.valueOf(from));
        }
    }

    private BigInteger countSequential(int from, int to) {
        return iterativeCounter.count(from, to);
    }
}
xingbin :

In RecursiveCounter, from * middle and (middle + 1) * to might overflow, you need use BigInteger to manipulate them:

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

Then you can get the same result in RecursiveCounter and LoopCounter:

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

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=473805&siteId=1