【问题标题】:Prime factor of 300 000 000 000?300 000 000 000 的质因数?
【发布时间】:2010-10-01 04:30:59
【问题描述】:

我需要找出超过 3000 亿的质因数。我有一个功能正在添加到它们的列表中......非常缓慢!它现在已经运行了大约一个小时,我认为它还有很长的路要走。我这样做是完全错误的还是预期的?

编辑:我试图找到数字 600851475143 的最大素数。

编辑: 结果:

{
    List<Int64> ListOfPrimeFactors = new List<Int64>();
    Int64 Number = 600851475143;
    Int64 DividingNumber = 2;

    while (DividingNumber < Number / DividingNumber)
    {
        if (Number % DividingNumber == 0)
        {
            ListOfPrimeFactors.Add(DividingNumber);
            Number = Number/DividingNumber;
        }
        else
            DividingNumber++;
        }
        ListOfPrimeFactors.Add(Number);
        listBox1.DataSource = ListOfPrimeFactors;
    }
}

【问题讨论】:

  • 请用适当的语言阐明您要解决的精确问题是什么。你所解释的可能至少有五六种可能的含义。另外,你有任何上限吗? 10^40000000000 是“超过 3000 亿”,可能确实需要几个小时...... :)
  • 你的意思是真的要找出超过 300 000 000 000 的素数吗? (作为一个主要因素完全是另外一回事)
  • 你是在解决项目欧拉的问题3吗?
  • 这是家庭作业吗?
  • (1) 阅读并完全理解en.wikipedia.org/wiki/Prime_number (2) 阅读并完全理解en.wikipedia.org/wiki/Prime_factor (3) 阅读并完全理解en.wikipedia.org/wiki/Integer_factorization (4) 开始思考算法 (5) 选择一个并编码

标签: algorithm prime-factoring fxsl


【解决方案1】:

这里有一些 Haskell 给你们的好处 :)

primeFactors n = factor n primes
  where factor n (p:ps) | p*p > n = [n]
                        | n `mod` p /= 0 = factor n ps
                        | otherwise = p : factor (n `div` p) (p:ps)
        primes = 2 : filter ((==1) . length . primeFactors) [3,5..]

花了大约 0.5 秒才找到它们,所以我称之为成功。

【讨论】:

  • 他?我是她!不,我确实在原始问题中要求没有代码,但它不是我使用的语言,所以它没有麻烦!
  • 如果不筛选,代码更少,速度更快:primeFactors n = factor n (2:[3,5..])
  • mod 和 div 周围的反引号没有出现。任何其他尝试使用此代码的人,请在 mod 和 div 周围加上“反引号”。 @一种。雷克斯:总是更快?或者只是在给定的情况下?随着输入数字变大,筛子会更有帮助吗?
【解决方案2】:

具体号码是300425737571?它微不足道地分解为 131 * 151 * 673 * 22567。 我看不出有什么大惊小怪的......

【讨论】:

    【解决方案3】:

    这是一个 XSLT 解决方案!

    此 XSLT 转换需要 0.109 秒

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns:saxon="http://saxon.sf.net/"
     xmlns:f="http://fxsl.sf.net/"
     exclude-result-prefixes="xs saxon f"
     >
     <xsl:import href="../f/func-Primes.xsl"/>
    
     <xsl:output method="text"/>
    
    
     <xsl:template name="initial" match="/*">
       <xsl:sequence select="f:maxPrimeFactor(600851475143)"/>
     </xsl:template>
    
     <xsl:function name="f:maxPrimeFactor" as="xs:integer">
       <xsl:param name="pNum" as="xs:integer"/>
    
       <xsl:sequence select=
        "if(f:isPrime($pNum))
           then $pNum
           else
             for $vEnd in xs:integer(floor(f:sqrt($pNum, 0.1E0))),
                 $vDiv1 in (2 to $vEnd)[$pNum mod . = 0][1],
                 $vDiv2 in $pNum idiv $vDiv1
               return 
                 max((f:maxPrimeFactor($vDiv1),f:maxPrimeFactor($vDiv2)))
        "/>
     </xsl:function>
    </xsl:stylesheet>
    

    这种转换只需 0.109 秒即可产生正确的结果(最大素因数 600851475143)。

    6857

    转换使用FXSL 2.0 中定义的f:sqrt()f:isPrime()——XSLT 中的函数式编程库。 FXSL 本身完全用 XSLT 编写。

    f:isPrime() 使用Fermat's little theorem,以便确定素数是有效的。

    【讨论】:

    • 太整洁了!太糟糕了,问题 3 论坛已关闭,因此您无法在此处发布此内容。我不认为我在那里见过 XSLT 解决方案。
    • 欧拉项目中的问题 3 (projecteuler.net/index.php?section=problems&id=3)。当您在该站点注册时,您会在该站点获得一个答案框,如果您提交正确的答案框(您显然会这样做),您将可以访问人们讨论问题及其解决方案的论坛。
    • 虽然问题是很久以前提出的,所以论坛已经“满员”,他们已经关闭了新条目。
    • @PEZ:感谢您提及 ProjectEuler。我已经解决了 28 个问题,我使用的唯一编程语言是 XSLT。
    • Euler 项目的问题论坛现已重新开放。虽然,这些新帖子不是永久性的——他们最多保留 100 个最近的帖子。在问题 3 中,这可以追溯到整个... 3 周,现在。 :)
    【解决方案4】:

    我花了一些时间来解决这个问题,因为它吸引了我。我暂时不会在此处粘贴代码。如果您好奇,请查看this factors.py gist

    请注意,在阅读此问题之前,我对因式分解一无所知(仍然不知道)。这只是上面BradC答案的Python实现。

    在我的 MacBook 上,计算问题中提到的数字 (600851475143) 需要 0.002 秒。

    显然必须有很多更快的方法来做到这一点。我的程序需要 19 秒来计算 6008514751431331 的因数。但 Factoris 服务很快就会给出答案。

    【讨论】:

    • 这是 Project Euler 网站 (projecteuler.net) 上的一个论坛,一旦您提交了我们在此讨论的问题的正确答案,您就可以访问该论坛。看看这个。但我警告你;很容易上瘾!
    • @PEZ:非常感谢您提到 ProjectEuler。我已经解决了 28 个问题,我使用的唯一编程语言是 XSLT。
    • 太棒了。你不能把你的解决方案放在 gist.github.com 或类似的地方。我希望看到针对我已解决的问题的更多 XSLT 解决方案。最后一个问题是上次发布的,所以如果你解决了那个问题,你可能可以在项目论坛上发帖。他们明天要提出一个新问题。
    【解决方案5】:

    理解为什么平方根很重要的关键是,考虑到n 低于 n 的平方根的每个因子都有一个对应的因子高于。要看到这一点,请考虑如果 x 是 n 的因数,则 x/n = m,这意味着 x/m = n,因此 m 也是一个因数。

    【讨论】:

      【解决方案6】:

      只是为了稍微扩展/改进“仅测试不以 5 结尾的奇数”的建议...

      所有大于 3 的素数要么比 6 的倍数大一或小一(对于 x 的整数值为 6x + 1 或 6x - 1)。

      【讨论】:

        【解决方案7】:

        使用蛮力很难找到主要因素,这听起来像您正在使用的技术。

        以下是一些加快速度的技巧:

        • 起步低,不高
        • 不必费心测试每个潜在因素以查看它是否为素数 - 只需测试 LIKELY 素数(以 1、3、7 或 9 结尾的奇数)
        • 不要费心测试偶数(都可以被 2 整除)或以 5 结尾的赔率(都可以被 5 整除)。当然,实际上不要跳过 2 和 5!!
        • 当你找到一个素因数时,一定要把它分开——不要继续使用你原来的大数。请参阅下面的示例。
        • 如果您发现某个因素,请确保再次对其进行测试以查看它是否存在多次。您的号码可能是 2x2x3x7x7x7x31 或类似的数字。
        • 达到 >= sqrt(剩余大数)时停止

        编辑:一个简单的例子: 你正在寻找 275 的因数。

        1. 测试 275 是否能被 2 整除。275/2 = int(275/2) 吗?否。失败。
        2. 测试 275 的可被 3 整除。失败。
        3. 跳过 4!
        4. 测试 275 被 5 整除。是的! 275/5 = 55。所以您的新测试编号现在是 55。
        5. 测试 55 是否能被 5 整除。是的! 55/5 = 11。所以您的新测试编号现在是 11。
        6. 但是 5 > sqrt (11),所以 11 是素数,你可以停下来!

        所以 275 = 5 * 5 * 11

        更有意义?

        【讨论】:

        • 你是什么意思'分开'?我不确定平方根如何影响它?
        • 如果你还没有通过一个数的平方找到一个因子,那么无论你走多远,你都找不到任何因子。这在检查更大的数字时会产生巨大的差异。
        • 你为什么说“至少所有以 1,3,7 或 9 结尾的奇数”?
        • 您必须在浪费与跳过每 5 个数字所需的努力之间取得平衡。跳过 3 之后的每第三个奇数,您可能会获得更多好处。请注意,从计算机的角度来看,十进制中以 5 结尾的数字并没有什么特别之处。
        • @Grace,对于你的后一个问题,是的,我想是的。这里有两个问题:“编程”和“解决现实世界的问题”。在您的情况下,现实世界的问题是“质数”,您应该做的第一件事就是研究有关它们的理论。然后考虑算法。
        【解决方案8】:

        最快的算法是筛算法,并且基于离散数学的神秘领域(至少在我的脑海中),实现和测试很复杂。

        最简单的因式分解算法可能是(正如其他人所说)埃拉托色尼筛法。使用它来分解数字 N 时要记住的事情:

        • 总体思路:您正在检查可能的整数因子x 的递增序列,以查看它们是否均分您的候选编号N(在C/Java/Javascript 中检查是否N % x == 0)在这种情况下,N 是不是素数。
        • 你只需要上升到sqrt(N),但实际上不要计算sqrt(N):只要你的测试因子x通过测试x*x&lt;N循环
        • 如果你有记忆保存一堆以前的素数,只使用那些作为测试因子(如果素数 P 未通过测试,请不要保存它们P*P &gt; N_max,因为你永远不会再使用它们了
        • 即使您不保存以前的素数,对于可能的因数x,只需检查 2 和所有奇数。是的,它需要更长的时间,但对于合理大小的数字来说不会更长。 prime-counting function 及其近似值可以告诉您哪些数字是素数;这部分减少非常缓慢。即使对于 264 = 大约 1.8x1019,大约每 43 个数字中有一个是素数(= 每 21.5 个奇数中有一个是素数)。对于小于 264 的数字因子,那些因子 x 小于 232,其中大约每 20 个数字中有一个是素数 = 每 10 个奇数中有一个数字是素数。因此,您必须测试 10 倍的数字,但循环应该更快一些,而且您不必费心存储所有这些素数。

        还有一些更老、更简单的筛选算法,它们稍微复杂一些,但仍然相当容易理解。请参阅Dixon'sShanks'Fermat's 分解算法。我曾经读过一篇关于其中一个的文章,不记得是哪一篇了,但它们都相当简单,并且使用了平方差的代数性质。

        如果您只是测试一个数字 N 是否为素数,而您实际上并不关心这些因素本身,请使用 probabilistic primality test。我认为Miller-Rabin 是最标准的。

        【讨论】:

        • 当我做 x*x
        • 如果 N 是固定的,也许解释器足够“聪明”,只评估一次......
        • 在分解的另一个线程中,我在 Sieve 上运行了一个测试——它绝对是 STUNK,即使面对一个没有考虑所有列出的技巧的粗略算法这里。 (并不是说我确定所有的技巧都是好的——成本可能比他们节省的要高!)
        • Jason,问题是我不经常计算 sqrt(N),只有当我找到一个因素时。这是经常发生的比较。也许比较 p 要有效得多(我的测试表明它要有效得多)
        【解决方案9】:

        您可以从以下网站获得答案:Factoris - Online factorization service。它可以处理非常大的数字,但它也可以分解代数表达式。

        【讨论】:

          【解决方案10】:

          您的算法必须是 FUBAR。在我使用 Python 编写的 1.6 GHz 上网本上,这只需要大约 0.1 秒。 Python 并不以其惊人的速度而闻名。但是,它确实具有任意精度的整数...

          import math
          import operator
          
          def factor(n):
              """Given the number n, to factor yield a it's prime factors.
              factor(1) yields one result: 1. Negative n is not supported."""
              M = math.sqrt(n)  # no factors larger than M
              p = 2             # candidate factor to test
              while p <= M:     # keep looking until pointless
                  d, m = divmod(n, p)
                  if m == 0:
                      yield p   # p is a prime factor
                      n = d     # divide n accordingly
                      M = math.sqrt(n)  # and adjust M
                  else:
                      p += 1    # p didn't pan out, try the next candidate
              yield n  # whatever's left in n is a prime factor
          
          def test_factor(n):
              f = factor(n)
              n2 = reduce(operator.mul, f)
              assert n2 == n
          
          def example():
              n = 600851475143
              f = list(factor(n))
              assert reduce(operator.mul, f) == n
              print n, "=", "*".join(str(p) for p in f)
          
          example()
          
          # output:
          # 600851475143 = 71*839*1471*6857
          

          (这段代码似乎无视我对数论的了解不足以填补顶针这一事实。)

          【讨论】:

          • 您的选择很幸运:71 显示得相当快,这大大减少了花费的时间。尝试考虑这个:12073531234081300153
          • 是的,当然,即使这个相当愚蠢的算法如果不浪费时间尝试非素数的 p 值也会更好。但是,我应该指出,问题中特别提到了 600851475143,因此它不是 luck。 ;-)
          【解决方案11】:

          您只需要检查它的余数 mod(n) 其中 n 是质数

          【讨论】:

          • 叹息不,我认为这只是我缺乏素数知识!
          • 在成为程序员之前,我是数论专业的博士生,看起来还有很多其他人也参与其中。正如其他帖子中提到的,质数是保证一切安全的原因。如果您有时间并且感兴趣,请查看。
          【解决方案12】:

          您可以使用sieve of Eratosthenes 来查找素数,看看您的数是否可以被您找到的数整除。

          【讨论】:

          • 与仅测试每个奇数相比,筛子非常慢。
          【解决方案13】:

          分解大数是一个难题。事实上,这太难了,以至于我们依靠它来保证 RSA 的安全。但是请查看wikipedia page 以获得一些可以提供帮助的算法指针。但是对于这么小的数字,真的不应该花那么长时间,除非你一遍又一遍地重新做你不必在某个地方做的工作。

          对于蛮力解决方案,请记住您可以进行一些小优化:

          • 专门检查2,然后只检查奇数。
          • 您只需要检查数字的平方根(如果到那时您没有找到任何因数,则该数字是素数)。
          • 一旦找到一个因数,就不要使用原来的数字来寻找下一个因数,将它除以找到的因数,然后搜索新的较小的数。
          • 找到一个因子后,尽可能多地对其进行划分。之后,您再也不需要检查该数字或任何较小的数字了。
          • 如果您执行上述所有操作,您找到的每个新因子都将是质数,因为所有较小的因子都已被删除。

          【讨论】:

          • 问题中的数字既不大也不难考虑。
          • 当我们说分解大数字很困难时,我们的意思是大约 2^512 以上的数字。 RSA 和类似的可能使用大约 2^2048 的素数。 OP 中的数字只有大约 2^39... 小得多
          【解决方案14】:

          您是否记得在找到它们时将要分解的数字除以每个因子?

          比如说,你发现 2 是一个因数。您可以将其添加到您的因子列表中,然后将您尝试分解的数字除以该值。

          现在您只搜索 1500 亿的因数。每次你都应该从你刚刚找到的因素开始。因此,如果 2 是一个因素,请再次测试 2。如果您找到的下一个因素是 3,那么再次从 2 进行测试是没有意义的。

          等等……

          【讨论】:

          • 哦,好的,我明白了!非常感谢!
          • 研究递归。
          • 质因数分解的递归?
          • 如果 BIGNUM = firstFactorFound*(BIGNUM/firstFactorFound),那么你可以分解出 BIGNUM/firstFactorFound 来得到其余的因子。此外,这个新数字的因数 >= firstFactorFound。您可以递归地应用它。
          • 这个过程是递归的,但我不会将它实现为递归函数,否则你会得到......一个stackoverflow;)使用迭代。
          【解决方案15】:

          最后一件事没有人提到,也许是因为它看起来很明显。每次你找到一个因素并把它分开时,继续尝试这个因素,直到它失败。

          64 只有一个质因数,2。如果你一直将 2 分开,直到你不能再除,你会发现它非常微不足道。

          【讨论】:

            【解决方案16】:
            $ time factor 300000000000 > /dev/null
            
            real        0m0.027s
            user        0m0.000s
            sys         0m0.001s
            

            如果需要一个小时,你就做错了。您甚至可能在某处出现无限循环 - 请确保您没有使用 32 位整数。

            【讨论】:

              【解决方案17】:

              这种大小的半素数用于加密,所以我很好奇你到底想用它们做什么。

              除此之外,目前还没有很好的方法可以在相对较短的时间内找到大数的素因数分解。

              【讨论】:

              • 在小于 40 位的部分为真,我的错误。但是,加密素数 iirc 通常每个为 512 位(生成的密钥总共为 1024 位)。您使用两个,将它们相乘,然后得到一个半素数(一个只有两个素因数的数字)。
              【解决方案18】:

              我根本不认为它需要很长时间 - 这不是一个特别大的数字。

              您能否给我们一个导致您的代码困难的示例编号?

              【讨论】:

              • 600851475143。我把它切成两半(我以为我从学校记得的)使它成为 300425737571。我去掉了可被 2、3、5 和 7 整除的数字。然后我去掉了任何不能被数字除的数字并找到剩下的任何素数...
              • “600851475143。我把它切成两半(我以为我从学校记得的)使它成为 300425737571。”对不起????如果你不知道问题的基本语义,你甚至不应该尝试。
              • 你会“试一试”把它切成两半吗?
              • 600851475143 和 300425737571 各有四个不同的质因数。他们有没有的共同点。 @Daniel Daranas 建议您理解为什么“将 [奇数] 切成两半”不起作用。例如,您可以尝试对 27 进行相同操作,它会“削减”为 13,但与 13 没有任何因子。
              • @A.雷克斯感谢您的澄清。是的,我所说的基本上是您可以“尝试”任何方法,但如果它们在数学上是正确的,它会有所帮助。将数字除以 7.65 然后取其对数当然没有帮助。
              【解决方案19】:

              即使使用相对幼稚的蛮力,也不应该花那么长时间。对于这个特定的数字,我可以在大约一秒钟内将其计入脑海。

              您说您不想要解决方案(?),但这是您的“微妙”提示。数的唯一素因数是最低的三个素数。

              【讨论】:

              • 我几乎不想知道这个问题的答案......但是为什么你需要这个特定数字的因素?
              猜你喜欢
              • 2020-10-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-07-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多