简体   繁体   中英

Stack-overflow exception on probabilistic calculation

I was doing problem 14 on Project Euler (note: I'm not looking for a solution to the Project Euler problem) when I ran into an interesting stack overflow exception.

My non-probabilistic approach worked just fine but when I attempted the same problem with a probabilistic approach I ran into the stack overflow exception. The funny thing is that the exception only occurs about 17% of the times. A thousand run-throughs yielded 166 exceptions.

I know my probabilistic logic is flawed, but I'm more interested in the cause of the exceptions and ways to prevent them from occurring. Do I simply need to do some memory management, maybe set some variables to null after using them? If so where would the key-points be to do so?

The code is as follows:

public class Problem14_LongestCollatzSequence {

    private static final int STARTING_CHAIN_LENGTH = 1;
    private static final int PROBABLY_RIGHT = 100000;

    /**
     * Calculate and return the Collatz sequence of a given number.
     *
     * @param number The number for which the Collatz sequence is to be
     * calculated.
     * @param chainlength The length of the chain for the number. This should
     * start with an initial value of 1.
     * @return The Length of the Collatz sequence.
     */
    private static int getChainLength(long number, int chainlength) {
        // All chains should end with 1.
        if (number != 1) {
            // If the number is even, halve the number, otherwise multiply it by 3 and add 1.
            if (number % 2 == 0) {
                number = number / 2;
            } else {
                number = number * 3 + 1;
            }
            // Call this function again.
            return getChainLength(number, ++chainlength);
        }
        // Return the length of the chain.
        return chainlength;
    }

    /**
     * Determine and return the number below a maximum value that will result in
     * the longest Collatz chain.
     *
     * @param maxStartingNumber The maximum value (exclusive) of the numbers
     * that will be tested.
     * @return The number that will produce the longest Collatz sequence in the
     * given range.
     */
    private static int calculateLongestChain(int maxStartingNumber) {
        Random random = new Random();
        int probabilityCounter = 0;
        int currentChainNumber = 0;
        int longestChainNumber = 0;
        int currentChainLength = 0;
        int longestChainLength = 0;

        // Get the chain length of random numbers until a certain number of unsuccsessful attempts have been made.
        while (probabilityCounter <= PROBABLY_RIGHT) {
            currentChainNumber = random.nextInt(maxStartingNumber);
            currentChainLength = getChainLength(currentChainNumber, STARTING_CHAIN_LENGTH);
            // If the current chain-length is bigger than the previously calculated one, reset the counter and update the chain number, otherwise increase the counter.
            if (currentChainLength > longestChainLength) {
                probabilityCounter = 0;
                longestChainLength = currentChainLength;
                longestChainNumber = currentChainNumber;
            } else {
                ++probabilityCounter;
            }
        }
        return longestChainNumber;
    }

    private static int calculateLongestChainNP(int maxStartingNumber) {
        // Non-probabilistic way to calculate the longest Collatz sequence.
        int currentChainLength = 0;
        int longestChainLength = 0;
        int longestChainNumber = 0;
        // Simply loop through all the numbers in the range to calculate the one resulting in the longest sequence.
        for (int i = 1; i < maxStartingNumber; i++) {
            currentChainLength = getChainLength(i, STARTING_CHAIN_LENGTH);
            if (currentChainLength > longestChainLength) {
                longestChainLength = currentChainLength;
                longestChainNumber = i;
            }
        }
        return longestChainNumber;
    }

    public static void main(String[] args) {
        int exceptionCount = 0;
        for (int count = 0; count < 1000; count++) {
            try {
                int testNumber = 1000000;
                System.out.println("Probabilistic answer: " + calculateLongestChain(testNumber));
                System.out.println("Non-probabilistic answer: " + calculateLongestChainNP(testNumber) + "\n");
            } catch (java.lang.StackOverflowError soe) {
                exceptionCount++;
                System.err.println(soe + "\n");
            }
        }
        System.out.println("Exception count: " + exceptionCount);
    }
}

I wanted to provide the full output as well, but that puts me over the character limit.

You will see in your stackoverflow exception the cause of the exception. In this case it is too much recursion and you will see it by a repeating stackframes in the stacktrace.

Try to make your algorithm iterative instead of recursive and your problem is solved.

Your recursion is too deep. You can increase call stack on your JVM with -Xss 4096m , but this is brute force. Be more elegant and use a while loop instead of recursion in getChainLength() :

private static int getChainLength(long number, int chainlength) {
        // All chains should end with 1.
        while (number != 1) {
            // If the number is even, halve the number, otherwise multiply it by 3 and add 1.
            if (number % 2 == 0) {
                number = number / 2;
            } else {
                number = number * 3 + 1;
            }
            // Call this function again.
            ++chainlength;
        }
        // Return the length of the chain.
        return chainlength;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM