关于递归:Java:找出数字是否为素数

Java: Find out if a number is prime recursively

我正在编写一个函数,如果数字是素数,则返回true,否则返回false

这是我当前的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public static boolean checkPrime(int n, int currDivisor){
        if(n < 2){
            return true;
        }
        if(currDivisor == (n/2)){
            return true;
        }
        else if(n % currDivisor == 0 ){
            return false;          
        }
        else{
            return checkPrime(n, currDivisor + 1);
        }
    }

    public static void main(String[] args){
        System.out.println(checkPrime(23352, 2));
    }

它适用于许多测试用例,但" 1000000007"之类的数字会出现内存不足错误。 如何调整此代码以提高空间效率?


根本问题是递归不是正确的方法。原始性测试不是递归问题,对于大量存储,您将始终很快超过可用存储。我建议您在网络上进行有关"原始性测试"的研究。

关于确定问题是否递归的经验法则,我已经做了很久了,我不确定我能否表达出已经完全直观的内容,所以我会让别人去做。

但是,值得指出的是,某些数学上递归的问题具有计算解决方案,其迭代远比幼稚的递归好得多。素数(哈!)的例子是斐波那契数。对于较大的n,天真的递归解决方案会占用内存并执行冗余计算,而迭代解决方案在整个过程中都更快,更好。


我看到的第一个问题是您的程序是越野车。似乎认为0、1,&4是质数,而3不是。我看到的第二个问题是,它浪费了堆栈帧,在递归之前没有正确处理简单的情况。这是我对您的代码的重做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static boolean checkPrime(int n) {
    return checkPrime1(n, 3);
}

public static boolean checkPrime1(int n, int currDivisor) {
    if (n < 2) {
        return false;
    }

    if (n % 2 == 0) {
        return (n == 2);
    }

    if (currDivisor * currDivisor > n) {
        return true;
    }

    if (n % currDivisor == 0) {
        return false;
    }

    return checkPrime1(n, currDivisor + 2);
}

至于处理:

1
System.out.println(checkPrime(1000000007));

您仍然会得到一个java.lang.StackOverflowError,但这还不是故事的结尾。大多数语言都会决定要分配给特定用途的内存量。这是像Perl这样的罕见语言,它将重新分配内存给最需要它的资源,而不会对程序的行为做任何假设。

您可以更改分配给Java堆栈的内存量-使用-Xss2m参数调用java会分配足够的额外堆栈,以供您测试1000000007(顺便说一下,true)。

如果将上面的三个int声明更改为long,则只要扩展堆栈(在这种情况下为-Xss4m),就可以测试类似2547487897L或更大的数字。

我并不是说这是递归的好问题,不是。但是,如果您要使用递归,请明智地使用它。不好的递归使递归变得不好名。递归的Fibonnaci算法效率不高(通常是双重递归),高效的(单一递归)算法也有。递归代码通常最适合递归数据。

某些语言(不是Java一致的语言)可以将上述代码作为尾部递归进行优化,并使其具有有效的迭代性能。