【问题标题】:Why did this prime factorisation algorithm give the correct answer even though there is a flaw?为什么这个素数分解算法即使有缺陷也能给出正确的答案?
【发布时间】:2013-05-14 07:49:56
【问题描述】:

对我说的问题是这样的:

“600851475143 的最大质因数是多少?”

该程序用于查找答案正是使用 C:

#include<math.h> // for remainder because % does not work with double or floats
#include<stdio.h>  
main()
{
    double x=600851475143,y=3.0;
    while(x!=y)                         // divide until only the number can divide itself
    {
        if(remainder((x/y),1)==0.0)     // if x is divisible by y which means it is a factor then do the magic
        {
            x=x/y;                      // divide the number x by y thereby reducing one constituent factor
        }
        y=y+2;                          // add 2 simply because only odd numbers can be prime and hence only prime numbers can be prime factors
    }
    printf("%lf",y);                    // do the printing magic
}

问题正是要问你,我试图检查 x 除以所有奇数,但请注意,并非所有奇数都不是素数,算法中的这个缺陷应该会导致答案错误,因为实际上我应该检查主要因素(不是奇数因素)。

令人惊讶的是这个程序吐出的答案是正确的,我检查了答案。

我该如何解决这个问题?这没有意义。

【问题讨论】:

  • 算法不正确并不意味着它必须总是给出错误的结果。就像程序崩溃不需要未定义的行为一样。顺便说一句,您最好避免使用浮点数来解决与整数相关的问题。使用if (x % y == 0) 检查 x 是否可以被 y 整除。 最重要的是:格式化您的代码。
  • 没什么——只是在这种情况下,在大多数平台上使用ints 将不起作用,并且 OP 可能没有意识到这一点。
  • @xabhisan:做得好,但使用int64_t 比切换到浮点更合适,这会带来一系列完全不同的问题。
  • 代码存在更多问题:(1) 2 是质数。 (2)应该计算两次的因素呢(例如数字27,质数3应该计算3次)
  • @xabhisan 好吧,即使是坏掉的时钟也会一天两次显示正确的时间。这个算法也是。

标签: c algorithm primes prime-factoring


【解决方案1】:

请注意,该算法存在 3 个缺陷:

  1. 2 也是质数
  2. 它可能会除非素数和奇数(如 9)
  3. 它不会将质数除以一次以上(正如您所期望的那样,它会为诸如 27 之类的数字做)。

从这些我们可以得出结论,如果以下 2 个条件适用,被破坏的算法将产生正确的答案:

  1. 输入的数字是奇数(所以跳过 2 无关紧要)
  2. 让数字为n = p1*p2*...*p_k,其中p_i 都是质数。对于每个 j!=ip_i != p_j。在这里,这意味着实际上每个素数仅是输入数的一个因数,因此“避免”了问题 2+3。 (问题 2 - 为什么它被避免是微不足道的,问题 3 被避免是因为你已经将数字与所有相关的素因子相除,所以对于每个 m=p_i1*...*p_ic,因为这个数字已经除以所有素因子 - p_i1,...p_ic - 你将无法将其与m 分开。

【讨论】:

  • @Thomash 我不同意。例如 27 - 它会给你答案 9,这不是一个主要因素。我同意通过解决问题(3),这将成为一个无问题。
  • 您错误地声称该方法当且仅当 n=p₁*p₂*...*pₖ 其中pᵢ 是不同的素数时才有效。 only if 部分中的声明是错误的,因为该方法适用于某些非该形式的数字,例如 1161=27*43 得到正确的结果 43。此示例表明您的声明是错误的。
【解决方案2】:

既然其他人都在告诉你你的程序出了什么问题,我将给出一个使用试除法分解整数的正确算法:

function factors(n)
    f, fs := 2, []
    while f * f <= n
        if n % f == 0
            append f to fs
            n := n / f
        else f := f + 1
    append n to fs
    return fs

这解决了您的代码的两个问题。首先,它正确识别 2 的因子。其次,它返回所有因子及其多重性。

回答您关于除以非素数的问题:这是性能问题,而不是正确性问题。由于试验除数是按递增顺序测试的,因此在测试其组成素数时,任何复合除数都已从被分解的数字中删除。这意味着除以组合是没有用的,但不会影响结果。

当然,在处理整数时,你永远不应该使用浮点运算。在 C 语言中,一旦超出 long long 整数,您可能希望切换到 gmp 库。

有比试除法更好的算法来分解整数,也有比上面显示的更好的方法来实现试除法。但这是一个很好的起点。当你准备好更多时,我谦虚地在我的博客上推荐这篇文章Programming with Prime Numbers

【讨论】:

    【解决方案3】:

    之所以有效,是因为您的号码没有重复因素。

    600851475143 = 71 * 839 * 1471 * 6857
    

    尝试使用例如1573499 (23 * 37 * 43 * 43)。

    【讨论】:

    • 我希望看到这个算法在喂食 9 时锁定。
    • @Vesper 完全正确!但是在 225 上会产生一个非质因子,然后停止。
    【解决方案4】:

    如果测试的数字是偶数,那么程序将永远运行。 x 永远是偶数,y 永远是奇数,所以 x == y 永远不会为真。

    如果测试的数是奇素数或不同奇素数的乘积,则循环将找到除最后一个素数以外的所有素数并除以这些素数,直到只剩下最大的素数,循环将退出当 y 等于最大的素因子时,打印最大的素因子。

    有趣的情况是当测试的数字包含奇素数的平方、立方等因素时。循环会找到奇数素因数并除以它们,但例如,如果 3^2 是一个因数,它将除以 3,留下一个因数 3。具体发生的情况取决于素因数。如果只有一个质数平方因数 p^2,程序将不会结束。如果有一个立方体或两个正方形(p^3 或 p^2 q^2),则结果将是错误的剩余因子 p^2 或 pq,除非该数字具有比这更大的素因子。

    示例:3^2 x 5^2 x 13:找到因子 3、5 和 13,然后打印 15,这是错误的。 示例:3^2 x 5^2 x 17:找到因子 3、5 和 15,然后打印 17,恰好是正确的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-30
      相关资源
      最近更新 更多