在c中使用递归找到最大的素数

Posted

技术标签:

【中文标题】在c中使用递归找到最大的素数【英文标题】:finding greatest prime factor using recursion in c 【发布时间】:2012-01-30 02:20:43 【问题描述】:

已经编写了代码,我认为这是一个很好的算法,可以使用递归找到大量的最大素数。我的程序崩溃了,但任何大于 4 的数字都分配给了变量 huge_number。我不擅长递归,并且赋值不允许任何类型的循环。

#include <stdio.h>

long long prime_factor(int n, long long huge_number);

int main (void)

    int n = 2;
    long long huge_number =  60085147514;
    long long largest_prime = 0;

    largest_prime = prime_factor(n, huge_number);
    printf("%ld\n", largest_prime);

    return 0;


long long prime_factor (int n, long long huge_number)

    if (huge_number / n == 1)
        return huge_number;
    else if (huge_number % n == 0)
        return prime_factor (n, huge_number / n);        
    else
        return prime_factor (n++, huge_number);

任何关于它为什么崩溃以及我如何改进它的信息将不胜感激。

【问题讨论】:

【参考方案1】:

即使解决了使用后增量的问题以使递归永远持续下去,这也不适合递归解决方案 - 请参阅 here 了解原因,但归结为您可以多快减少搜索空间.

虽然您对 huge_number 的除法会很快减少它,但绝大多数递归调用都是通过简单地递增 n 来完成的。这意味着您将使用 很多 的堆栈空间。

你会更好:

使用不会爆栈的迭代解决方案(如果你只是想解决问题)(a);或 如果您只是想学习递归,请找到更适合递归的问题。

(a) 以您的递归解决方案为模型的这种野兽的一个示例是:

#include <stdio.h>

long long prime_factor_i (int n, long long huge_number) 
    while (n < huge_number) 
        if (huge_number % n == 0) 
            huge_number /= n;
            continue;
        
        n++;
    
    return huge_number;


int main (void) 
    int n = 2;
    long long huge_number =  60085147514LL;
    long long largest_prime = 0;

    largest_prime = prime_factor_i (n, huge_number);
    printf ("%lld\n", largest_prime);

    return 0;


从该迭代解决方案的输出中可以看出,最大因子是10976461。这意味着您的递归解决方案中的最后一批递归将需要一千万 百万 个堆栈帧的堆栈深度,而大多数环境都无法轻松应对。

如果你真的必须使用递归解决方案,你可以利用你没有检查的事实将堆栈空间减少到平方根一直到数字,但只到平方根。

另外,除了2之外,其他所有的素数都是奇数,所以你可以通过只检查两个加上奇数来进一步减半搜索空间。

考虑到这两件事的递归解决方案是:

long long prime_factor_r (int n, long long huge_number) 
    // Debug code for level checking.

    // static int i = 0;
    // printf ("recursion level = %d\n", ++i);

    // Only check up to square root.

    if (n * n >= huge_number)
        return huge_number;

    // If it's a factor, reduce the number and try again.

    if (huge_number % n == 0)
        return prime_factor_r (n, huge_number / n);

    // Select next "candidate" prime to check against, 2 -> 3,
    //   2n+1 -> 2n+3 for all n >= 1.

    if (n == 2)
        return prime_factor_r (3, huge_number);

    return prime_factor_r (n + 2, huge_number);

您可以看到我还删除了(在我看来很尴尬)构造:

if something then
    return something
else
    return something else

我更喜欢缩进量较小的代码:

if something then
    return something
return something else

但这只是个人喜好。无论如何,这会使您的递归级别降低到 1662(取消注释要验证的调试代码)而不是一千万,这是一个相当大的减少,但仍然不完美。这在我的环境中运行良好。

【讨论】:

这是一个基于递归的赋值,我不能使用任何形式的循环。这就是我卡住的地方。 @TristanPearce,您可以使用两个简单的技巧来降低递归级别要求(1)只检查 2 和奇数,以及(2)只检查 平方根 数字(这个才是真正的赢家) - 查看更新。 非常感谢。根据在线研究,我知道平方根与平方根有关。这比检查所有数字更有意义。 快速提问以避免编译器警告。对 long long 变量使用 %lld 是否合适? @TristanPearce,是的,您应该将 lld 用于 long long 类型。【参考方案2】:

您的意思是n+1,而不是n++n++ 使用它之后增加n,所以递归调用得到n 的原始值。

【讨论】:

我只是尝试了两种方法,但它仍然崩溃。使用 ++n 使得它需要额外的几秒钟。 @TristanPearce 是的,++n 可以工作,但请记住,正在递增的 n 的特定实例将永远不会再次使用(因为它是一个形式参数的调用递归调用后立即返回)。 @TristanPearce 代码实际上是正确的++nn+1。您的堆栈无法处理您给它的huge_number。尝试一个较小的数字 - 它现在适用于几十个。 @TristanPearce 最后提示:尝试在prime_factor 的顶部打印nhuge_number。这样你就可以看到代码在运行时做它的事情。 这仍然会破坏堆栈,仅仅是因为大量的递归属于 n++ 种类,而不是巨大的 /= n 种类。该数字的最后一个因子发现将需要大约一千万个堆栈级别。【参考方案3】:

您正在溢出堆栈,因为 n++ 后递增值,使用与当前调用中相同的值进行递归调用。

【讨论】:

【参考方案4】:

崩溃的原因是堆栈溢出。我在您的程序中添加了一个计数器并执行它(在 ubuntu 10.04 gcc 4.4.3 上),计数器在核心转储之前停止在“218287”。更好的解决方案是使用循环而不是递归。

【讨论】:

以上是关于在c中使用递归找到最大的素数的主要内容,如果未能解决你的问题,请参考以下文章

使用 D&C/递归的最大子数组

找到最大递归深度

这种在连续子数组中找到最大和的递归算法有啥优势吗?

在c ++中找到二维数组中的最大区域

使用递归找到最大产品

如果数字为负,为啥在递归解决方案中找到给定序列中的最大子序列的基本情况返回 0?