从鳕鱼中算出div。程序中的StackOverflowError使用递归

时间:2015-12-29 10:37:18

标签: java recursion stack-overflow

我为Codility的一堂课写了一个程序。它被称为计数div。

例如。我给出了数字6,11和2.有3个数字从6到11我们可以除以2,它的6,8,10这样的方法应该返回3.

起初我只使用int进行了递归程序,但是我得到了错误,所以我将其更改为BigIntegers,但它根本没有帮助。它适用于小数字,但例如输入:

A = 0,B = 20000,K = 1,它给出错误:

Exception in thread "main" java.lang.StackOverflowError
at java.math.MutableBigInteger.divideKnuth(Unknown Source)
at java.math.MutableBigInteger.divideKnuth(Unknown Source)
at java.math.BigInteger.remainderKnuth(Unknown Source)
at java.math.BigInteger.remainder(Unknown Source)
at java.math.BigInteger.mod(Unknown Source)
at count_div.Solution.bigIntegerSolution(Solution.java:29)
at count_div.Solution.bigIntegerSolution(Solution.java:35)

这是我的代码:

public int solution(int A, int B, int K){

    BigInteger minValue = BigInteger.valueOf(A);
    BigInteger maxValue = BigInteger.valueOf(B);
    BigInteger div = BigInteger.valueOf(K);

    finalCounter = bigIntegerSolution(minValue, maxValue, div).intValue();

    return finalCounter;
}

public BigInteger bigIntegerSolution(BigInteger minValue, BigInteger maxValue, BigInteger div){

    int comparator = minValue.compareTo(maxValue);

    if(comparator <= 0){

        BigInteger modValue = minValue.mod(div);

        if( modValue.compareTo(zero) == 0){
            divCounter = divCounter.add(one);
        }
        minValue = minValue.add(one);
        bigIntegerSolution(minValue, maxValue, div);
    }

    return divCounter;
}

我能做些什么或者我的解决方案想法对于这个目的是不是很糟糕?我知道他们是其他解决方案,但我首先想出了这个,我想知道我是否可以修复它。

10 个答案:

答案 0 :(得分:3)

递归对于这个问题不是一个很好的选择,因为当你浏览数字时,你真的没有很多状态存储。每次将范围增加1时,将深度增加1。因此,您的堆栈溢出错误很大。

你不需要BigInteger:它是堆栈的深度,而不是引起问题的变量的大小。

以下是使用递归的解决方案:

int divisorsInRange(int min, int max, int div) {
    if (min > max)
        return 0;
    else
        return (min % div == 0 ? 1 : 0) + divisorsInRange(min + 1, max, div);
}

非递归解决方案实际上更简单,更高效。例如,使用Java 8流:

return IntStream.range(min, max).filter(n -> n % div == 0).count();

但是,您也可以在没有任何循环或流的情况下解决此问题。

EDIT1:错误的解决方案,虽然似乎是正确和优雅的。查看以下@Bopsi提到的min = 16, max =342, div = 17

int countDivisors(int min, int max, int div) {
    int count = (max - min) / div;
    if (min % div == 0 || max % div == 0)
        count++;
    return count;
}

EDIT2:正确的解决方案:

int solution(int A, int B, int K) {
    const int firstDividableInRange = A % K == 0 ? A : A + (K - A % K);
    const int lastDividableInRange = B - B % K;
    const int result = (lastDividableInRange - firstDividableInRange) / K + 1;

return result;
}

答案 1 :(得分:1)

您的解决方案超出了初始要求

  

复杂度:

     

预期的最坏情况时间复杂度为O(1);
  预期的最坏情况空间复杂度为O(1)。

一线解决方案

public class CountDiv {
    public int solution(int a, int b, int k) {
        return b / k - a / k + (a % k == 0 ? 1 : 0);
    }
}

Test results

答案 2 :(得分:0)

B值越大,计算机内存中存储的BigIntegers就越多。这就是为什么它适用于小值,并且不适用于大值。因此,递归是解决这类问题的一个糟糕的解决方案,因为你试图在内存中存储太多的值。

答案 3 :(得分:0)

这是Java中的(100/100)解决方案。

class Solution {
    public int solution(int A, int B, int K) {
        int result;
        int toAdd = 0;
        int lowerBound = 0;
        int upperBound = 0;
        if (A % K == 0) {
            lowerBound = A;
            toAdd = 1;
        } else {
            lowerBound = A - A % K + K;
            if ((lowerBound - A % K) >= 0 ) {
                toAdd = 1;
            }
        }

        if (B % K == 0) {
            upperBound = B;
        } else {
            upperBound = B - B % K;
        }

        result = (upperBound - lowerBound) / K + toAdd;

        return result;
    }
}

答案 4 :(得分:0)

我能够使用算术级数(https://en.wikipedia.org/wiki/Arithmetic_progression)解决问题。我不得不为0添加一个特殊情况,我无法解释但它是基于测试结果:

if (K > B)
    return A == 0 ? 1 : 0;

int min = A >= K ? A + A % K : K;
int max = B - (B % K);

// an = a1 + (n − 1) * ⋅r
return (max - min + K) / K + (A == 0 ? 1 : 0);

答案 5 :(得分:0)

这是我的:)

public int solution(int A, int B, int K) {
    int numEl = 0;
    int first = A;
    while(numEl == 0 && first <= B) {
        if(first%K == 0) {
            numEl += 1;
        } else
           first += 1;
    }
    numEl += (B - first)/K;
    return numEl;
}

答案 6 :(得分:0)

按照代码注释获取清晰图片

public int solution(int A, int B, int K) {
        int start = 0;
        int end = 0;
        int count = 0;

        start = (A % K == 0)? A : ((A / K)* K ) + K; //minimum divisible by K in the range
        end = (B % K == 0)? B : B - (B % K); // maximum divisible by K in the range

        count = ((end - start) / K) + 1; //no of divisibles by K inside the range start & end

        return count;
    }

答案 7 :(得分:0)

这是我的解决方案。想法是,我正在寻找第一个可以在K上划分的数字,因为范围内的整除数是((B - first) / K) + 1。我们需要知道最大限制(B)和第一个整数之间的差异,并计算它们之间可以容纳多少K,因为不包括冷杉数,我们需要添加一个以获得正确结果

class Solution {

    public int solution(int A, int B, int K) {
        if (A == B) {
            if (A % K == 0) return 1;
            else return 0;
        }
        int first = (A % K == 0) ? A : A + (K - (A % K));
        if (A != 0 && (first > B || first == 0)) return 0;

        return ((B - first) / K) + 1;
    }
}

答案 8 :(得分:0)

简洁是红宝石。这也会得到100%

def solution(a, b, k)
  (b / k - (a - 1) / k).ceil
end

答案 9 :(得分:0)

我很难遵循这里的一些答案,尽管它们更优雅。但是这个解决方案让我能够更好地推理问题,也许对你有帮助。

基本思想是移动“A”和“B”值,直到它们与“K”整除性对齐。也就是说,我们移动 A & B 直到 (A % K == 0) 和 (B % K == 0)。

class Solution {
    public int solution(int A, int B, int K) {
        // shift A up
        while (A % K != 0) {
            A++;
        }

        // shift B down
        while (B % K != 0) {
            B--;
        }

        return (B - A) / K + 1;
    }
}

也可以在没有 while 循环的情况下完成,只需将 A 增加 mod 量,然后将 B 减少 K-mod 量。