递归地找到素数因子

时间:2017-03-23 10:16:36

标签: c recursion prime-factoring

我开始自学C并且正在尝试构建一组有用的功能和程序以供将来参考。如果相关,这是C99。

这是我最近的程序迭代。在输出中,我想得到数n的素因子化。但是,我会得到所有因素的列表,并且不会重复任何因素。我包含了一些打印语句来尝试和调试,并且发现错误与递归有关,但无法弄清楚如何进一步解释它。我之前尝试过使用int类型的递归函数,但很难让它使用数组p。

  • n是我想要考虑的数字
  • p是用于存储找到的素数的数组
  • j是p
  • 的索引
  • c是我作为n
  • 的除数测试的数字

我知道可能有更有效的方法来声明p来节省内存,但由于这主要是供参考,因此内存不是一个大问题。

我发现了这些问题,但并不认为他们回答了我的问题

我的主要问题是: 为什么输出显示n的所有因子? 为什么不重复主要因素? 我该怎么做才能解决它?

Stream

3 个答案:

答案 0 :(得分:0)

来自This answer

why does recursion cause stackoverflow so much more than loops do

因为每次递归调用都会在堆栈上使用一些空间。如果递归太深,那么它将导致StackOverflow,具体取决于堆栈中允许的最大深度。

使用递归时,您应该非常小心,并确保提供基本案例。递归中的基本情况是递归结束的条件,堆栈开始展开。这是导致StackOverflow错误的递归的主要原因。如果它没有找到任何基本情况,它将进入无限递归,这肯定会导致错误,因为Stack只是有限的。

-

看来你的for阻碍了,c会增加,并且不会再次检查相同的值。

例如,如果输入为8,我们需要(2,2,2)而不是(2,4)

我建议暂时替换你的if (c%n ==0),不要忘记在那个时候替换n的值,你不想在那里循环。

这似乎是一个很好的答案:

int primes(int nbr, int cur)
{
  if (cur > nbr)
    return (0);
  else
    {
      if (nbr % cur == 0 && isprime(cur) == 1)
        {
          printf("%d\n", cur);
          return (primes(nbr / cur, 2));
        }
      return (primes(nbr, cur + 1));
    }
}

在主

中使用cur = 2调用该函数

答案 1 :(得分:0)

编辑我发现n的最小因子保证是素数,所以我已经相应地编辑了答案。

  

为什么输出显示n的所有因子?

因为您测试c是否是n的因子并将其添加到数组p中c是否为素数。然后你继续测试c以上的数字,甚至是c的倍数。

  

为什么不重复主要因素?

因为当你发现数字c是一个因素时,你不必检查它以确定它本身是否为复合数。

将c添加到p后,您需要在factor上递归调用(n / c),然后停止。

这大致是你需要的(但未测试甚至编译)

int factorise(int n, int p[], int j)
{
    int factorsFound = 0;

    for (c = 2 ; c * c <= n && factorsFound == 0 ; ++ c)
    {
        if (n % c == 0)
        {
            p[j] = c;
            factorsFound = factorise(n / c, p, j + 1) + 1;
        }
    }
    if (factorsFound == 0) // n is prime
    {
        p[j] = n;
        factorsFound = 1;
    }
    return factorsFound;
}

同样在真正的解决方案中,您可能希望传递p的大小,以便您可以检测是否空间不足。

只是为了好玩,因为还没有其他人发布它,这是一个非递归的解决方案。它实际上与上面相同,但递归已转换为循环。

int factorise(int number, int p[])
{
    int j = 0;

    for (int c = 2, int n = number ; n > 1 ; )
    {
        if (n % c = 0)
        {
            p[j++] = c;
            n = n / c;
        }
        else
        {
            c++;
        }
    }
    return j;
}

我不同意Lundin关于递归的一些评论。递归是将问题分解为更容易的子任务的一种自然方式,但在C中,它无可否认效率较低,尤其是在堆栈空间方面,在这种特殊情况下,非递归版本更简单。

答案 2 :(得分:0)

你已经在评论中告诉你这个算法很差,这是一个证据。你真的应该学会使用调试器:通过调试器运行它会立即显示问题所在。

话虽这么说,你的主要问题是当递归函数返回时该做什么?。你没有问自己这个问题在递归中是必须的,只是按顺序继续,这是完全错误的,因为你将重用已经在递归调用中处理过的数字。所以你必须在递归调用factors之后立即添加一个返回行。

一旦完成,还有另一个小问题(调试器会显而易见),你只搜索严重小于n的因子。所以你错过了最后一个主要因素......

通过这两个即时修复,您的代码变为:

void factors(int n, int p[], int j) {
  /// if n is divisible by c, store c, and continue with n/c
      int c;
      for (c=2; c <= n; c++) {
          if (c > n) break;
          if (n%c == 0) {
              p[j] = c;
              printf("%d has been added to p \t", c);
              printf("n has been reduced to %d \t", n/c);
              printf("j is %d \n", j);
              j++;
              if (n == c) break;
              factors(n/c, p, j);
              return;
          }
      }
  }

但恕我直言p[j] = c;应成为*p = c;factors(n/c, p, j);应成为factors(n/c, p+1, j);。换句话说,您直接传递指向下一个插槽的指针