求数最大质因数的算法

Algorithm to find Largest prime factor of a number

提问人:mercutio 提问时间:8/23/2008 最后编辑:smcimercutio 更新时间:10/4/2023 访问量:237091

问:

计算数字最大质因数的最佳方法是什么?

我认为最有效的方法如下:

  1. 找到除以干净的最小素数
  2. 检查除法结果是否为素数
  3. 如果没有,请查找下一个最低值
  4. 转到 2。

我之所以做出这个假设,是因为计算小质因数更容易。这是对的吗?我还应该研究哪些其他方法?

编辑:我现在已经意识到,如果有超过 2 个质因数在起作用,我的方法是徒劳的,因为当结果是另外两个素数的乘积时,第 2 步会失败,因此需要递归算法。

再次编辑:现在我意识到这仍然有效,因为最后找到的素数必须是最高的素数,因此对步骤 2 的非素数结果的任何进一步测试都会导致更小的素数。

算法 数学 素因式分解

评论

0赞 Kevin Meredith 7/28/2014
我的方法是:(1)将大的、可能的数字除以 2;(2)检查大数是否均匀地划分进去;(3) 如果是这样,请检查除以 2 的数字是否为质数。如果是,请归还。(4)否则,从除以2的数字中减去1,返回步骤3。
0赞 user3819867 9/12/2015
1.找到任何除以清楚的数(因为 i = 2 到 int(sqr(num)) ),除以该数 (num = num/i),然后重复,直到在 1 中找不到任何东西s 区间 num 是最大的因子2.3.
1赞 7/24/2016
我们可以用小素数进行除法,最后剩下的就是最大的素数(我猜)

答:

-5赞 Klesk 8/23/2008 #1

我认为最好将所有可能的素数都存储在比 n 小的地方,然后迭代它们以找到最大的除数。您可以从 prime-numbers.org 获得素数。

当然,我认为你的数字不会太大:)

-3赞 palotasb 8/28/2008 #2

这可能并不总是更快,但更乐观的是,你找到了一个大的质数除数:

  1. N是您的号码
  2. 如果它是素数,那么return(N)
  3. 计算素数直到Sqrt(N)
  4. 按降序遍历素数(最大的在前)
    • 如果那么N is divisible by PrimeReturn(Prime)

编辑:在第 3 步中,您可以使用埃拉托色尼筛子或阿特金斯筛子或任何您喜欢的东西,但筛子本身不会发现您是最大的质因数。(这就是为什么我不会选择 SQLMenace 的帖子作为官方答案......

评论

1赞 Jonathan Leffler 7/16/2016
难道你不需要做试验分解来确定它是否是质数(步骤2)吗?此外,考虑找到 15 的最大质因数。sqrt(15) 之前的素数是 2 和 3;但最大的质因数是 5,不是吗?与 20 类似。
3赞 Ralph M. Rickenbach 10/15/2008 #3
n = abs(number);
result = 1;
if (n mod 2 == 0) {
 result = 2;
 while (n mod 2 = 0) n /= 2;
}
for(i=3; i<sqrt(n); i+=2) {
 if (n mod i == 0) {
   result = i;
   while (n mod i = 0)  n /= i;
 }
}
return max(n,result)

有一些模检验是肤浅的,因为如果去除了所有因子 2 和 3,n 就永远不能被 6 整除。你只能允许 i 的素数,这在这里的其他几个答案中都有显示。

你实际上可以在这里将埃拉托色尼的筛子交织在一起:

  • 首先创建整数列表 自。sqrt(n)
  • 在 for 循环中标记所有倍数 的 I 到 新的 as not prime,并改用 while 循环。sqrt(n)
  • 将 i 设置为 列表。

另请参阅此问题

146赞 Artelius 10/28/2008 #4

实际上,有几种更有效的方法来找到大数的因数(对于较小的因数,试除法效果相当好)。

如果输入数字有两个非常接近其平方根的因子,则有一种非常快的方法称为费马因式分解。它利用恒等式 N = (a + b)(a - b) = a^2 - b^2,易于理解和实现。不幸的是,它通常不是很快。

对长达 100 位的数字进行因式分解的最著名方法是二次筛。作为奖励,部分算法可以通过并行处理轻松完成。

我听说过的另一种算法是 Pollard 的 Rho 算法。它通常不如二次筛高效,但似乎更容易实现。


一旦你决定了如何将一个数字分成两个因子,这是我能想到的最快的算法,可以找到一个数字的最大质因数:

创建一个优先级队列,该队列最初存储号码本身。每次迭代,您都会从队列中删除最大的数字,并尝试将其拆分为两个因子(当然,不允许 1 成为这些因子之一)。如果这一步失败了,这个数字是素数,你有你的答案!否则,将这两个因素添加到队列中并重复。

评论

3赞 tmyklebu 9/23/2014
Pollard rho 和椭圆曲线法比二次筛更能去除数的小质因数。无论数量如何,QS 的运行时间都大致相同。哪种方法更快取决于您的数字;QS 将更快地破解难以因式分解的数字,而 rho 和 ECM 将更快地破解易于因式分解的数字。
0赞 dors 4/3/2017
感谢您的二次变分建议。我需要为我的一个客户实现这个,我提出的初始版本与@mercutio在他的问题中建议的内容大致相同。二次解是现在在 math.tools/calculator/prime-factors 上为我的客户工具提供动力的原因。
0赞 BenKoshy 4/24/2020
如果有一种有效的方法可以解决这个问题,那不意味着网络加密不安全吗?
4赞 Apocalisp 10/28/2008 #5

最简单的解决方案是一对相互递归的函数。

第一个函数生成所有质数:

  1. 从大于 1 的所有自然数的列表开始。
  2. 删除所有非素数。也就是说,没有质因数(除了它们本身)的数字。见下文。

第二个函数按递增顺序返回给定数的质因数。n

  1. 列出所有素数(见上文)。
  2. 删除所有不是 的因式数的数字。n

的最大质因数是第二个函数给出的最后一个数字。n

此算法需要惰性列表或具有按需调用语义的语言(或数据结构)。

为了澄清起见,以下是 Haskell 中上述的一个(低效)实现:

import Control.Monad

-- All the primes
primes = 2 : filter (ap (<=) (head . primeFactors)) [3,5..]

-- Gives the prime factors of its argument
primeFactors = factor primes
  where factor [] n = []
        factor xs@(p:ps) n =
          if p*p > n then [n]
          else let (d,r) = divMod n p in
            if r == 0 then p : factor xs d
            else factor ps n

-- Gives the largest prime factor of its argument
largestFactor = last . primeFactors

要使这更快,只需更聪明地检测哪些数字是质数和/或因数,但算法保持不变。n

4赞 nickf 10/28/2008 #6

所有数字都可以表示为素数的乘积,例如:

102 = 2 x 3 x 17
712 = 2 x 2 x 2 x 89

你可以通过简单地从 2 开始并继续除法,直到结果不是你的数字的倍数来找到这些:

712 / 2 = 356 .. 356 / 2 = 178 .. 178 / 2 = 89 .. 89 / 89 = 1

使用这种方法,您不必实际计算任何素数:它们都是素数,因为您已经尽可能多地使用前面的所有数字对数字进行了分解。

number = 712;
currNum = number;    // the value we'll actually be working with
for (currFactor in 2 .. number) {
    while (currNum % currFactor == 0) {
        // keep on dividing by this number until we can divide no more!
        currNum = currNum / currFactor     // reduce the currNum
    }
    if (currNum == 1) return currFactor;    // once it hits 1, we're done.
}

评论

0赞 wnoise 10/28/2008
是的,但这效率非常低。一旦你把所有的 2 都除掉了,你真的不应该尝试除以 4、6 或......;在限制范围内,只检查素数或使用一些算法确实更有效。
6赞 Kenan Banks 1/8/2009
+1 抵消 wnoise,我认为他错了。尝试除以 4 只会发生一次,并且会立即失败。我不认为这比从某个候选人列表中删除 4 个更糟糕,而且肯定比事先找到所有素数更快。
2赞 Kenan Banks 1/8/2009
@Beowulf。在投票否决之前,请尝试运行此代码。它返回质因数;你只是不了解算法。
3赞 blabla999 1/14/2009
代码工作正常,但如果传入号码是素数,则速度很慢。我也只会跑到正方形并增加 2。但是,对于非常大的数字来说,它可能太慢了。
4赞 Will Ness 4/12/2014
blabla999 是完全正确的。示例为 1234567898766700 = 2*2*5*5*12345678987667。当我们到达 时,我们知道 12345678987667 是素数,应该将其作为答案返回。相反,此代码将继续枚举,直到12345678987667本身。这比必要的速度慢了 3,513,642 倍。currFactor = 3513642
-1赞 Loren Pechtel 10/28/2008 #7

在我看来,给定算法的步骤 #2 不会是那么有效的方法。你没有合理的期望它是素数。

此外,先前的答案暗示了埃拉托色尼的筛子是完全错误的。我刚刚写了两个程序来分解123456789。一个基于筛子,一个基于以下内容:

1)  Test = 2 
2)  Current = Number to test 
3)  If Current Mod Test = 0 then  
3a)     Current = Current Div Test 
3b)     Largest = Test
3c)     Goto 3. 
4)  Inc(Test) 
5)  If Current < Test goto 4
6)  Return Largest

这个版本比 Sieve 快 90 倍。

问题是,在现代处理器上,操作类型远不如操作数量重要,更不用说上面的算法可以在缓存中运行,而 Sieve 不能。Sieve 使用了很多操作来剔除所有合数。

另请注意,我在确定因素时对因素进行划分减少了必须测试的空间。

评论

0赞 nickf 10/28/2008
我就是这么说的,但:(被否决了我想问题是,如果这个数字有一个非常大的质因数(比如它本身),那么这种方法必须一直循环到那个数字。不过,在很多情况下,这种方法非常有效。
0赞 Loren Pechtel 10/29/2008
回过头来读你的是一样的,但你的第一部分令人困惑。
0赞 Kladskull 5/9/2009
143816789988504044536402352738195137863656439在这个号码上试试,让我知道这有多高效......
152赞 Kenan Banks 1/5/2009 #8

这是我所知道的最好的算法(在 Python 中)

def prime_factors(n):
    """Returns all the prime factors of a positive integer"""
    factors = []
    d = 2
    while n > 1:
        while n % d == 0:
            factors.append(d)
            n /= d
        d = d + 1

    return factors


pfs = prime_factors(1000)
largest_prime_factor = max(pfs) # The largest element in the prime factor list

上述方法在最坏的情况下运行(当输入是质数时)。O(n)

编辑:
以下是评论中建议的版本。这里又是代码。
O(sqrt(n))

def prime_factors(n):
    """Returns all the prime factors of a positive integer"""
    factors = []
    d = 2
    while n > 1:
        while n % d == 0:
            factors.append(d)
            n /= d
        d = d + 1
        if d*d > n:
            if n > 1: factors.append(n)
            break
    return factors


pfs = prime_factors(1000)
largest_prime_factor = max(pfs) # The largest element in the prime factor list

评论

11赞 Kenan Banks 1/8/2009
在投票否决之前,请阅读和/或运行此代码。它工作正常。只需复制和粘贴即可。如所写,prime_factors(1000) 将返回 [2,2,2,5,5,5],应解释为 2^3*5^3,又名质因式分解。
11赞 Sheldon L. Cooper 9/26/2010
“在最坏的情况下运行” - 不,它在最坏的情况下运行(例如,何时是素数。O(sqrt(n))O(n)n
19赞 Sumudu Fernando 3/19/2012
很容易使它成为 O(sqrt(n)),您只需在 d*d > n 时停止循环,如果此时 n > 1,则其值应附加到质因数列表中。
5赞 Forethinker 7/20/2013
这个有名字吗?
16赞 tailor_raj 11/19/2013
由于 2 是唯一的偶数素数,因此您可以单独迭代 d=2,然后将其递增 1,然后从 d=3 开始递增 2,而不是每次都加 1。所以它会减少迭代的次数...... :)
18赞 Sundar R 5/6/2009 #9

我的答案是基于三联画的,但改进了很多。它基于这样一个事实,即超过 2 和 3,所有素数都是 6n-1 或 6n+1 的形式。

var largestPrimeFactor;
if(n mod 2 == 0)
{
    largestPrimeFactor = 2;
    n = n / 2 while(n mod 2 == 0);
}
if(n mod 3 == 0)
{
    largestPrimeFactor = 3;
    n = n / 3 while(n mod 3 == 0);
}

multOfSix = 6;
while(multOfSix - 1 <= n)
{
    if(n mod (multOfSix - 1) == 0)
    {
        largestPrimeFactor = multOfSix - 1;
        n = n / largestPrimeFactor while(n mod largestPrimeFactor == 0);
    }

    if(n mod (multOfSix + 1) == 0)
    {
        largestPrimeFactor = multOfSix + 1;
        n = n / largestPrimeFactor while(n mod largestPrimeFactor == 0);
    }
    multOfSix += 6;
}

我最近写了一篇博客文章,解释了这个算法是如何工作的。

我敢说,一种不需要测试原始性(并且没有筛子结构)的方法会比使用这些方法的方法运行得更快。如果是这样的话,这可能是这里最快的算法。

评论

12赞 Tobias Kienzler 10/7/2013
你实际上可以更进一步地理解这个想法,例如,超过 2,3,5 所有素数都是 30n+k (n >= 0) 的形式,其中 k 只取 1 到 29 之间不能被 2,3 或 5 整除的值,即 7,11,13,17,19,23,29。到目前为止,你甚至可以在找到几个素数后动态地适应它,即 2*3*5*7*...*n+k,其中 k 不能被这些素数中的任何一个整除(请注意,并非所有可能的 k 都需要是素数,例如,对于 210n+k,你必须包括 121,否则你会错过 331)
2赞 Nader Ghanbari 7/16/2014
我想应该是while (multOfSix - 1 <= n)
-6赞 Chitransh 10/9/2011 #10
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include <time.h>

factor(long int n)
{
long int i,j;
while(n>=4)
 {
if(n%2==0) {  n=n/2;   i=2;   }

 else
 { i=3;
j=0;
  while(j==0)
  {
   if(n%i==0)
   {j=1;
   n=n/i;
   }
   i=i+2;
  }
 i-=2;
 }
 }
return i;
 }

 void main()
 { 
  clock_t start = clock();
  long int n,sp;
  clrscr();
  printf("enter value of n");
  scanf("%ld",&n);
  sp=factor(n);
  printf("largest prime factor is %ld",sp);

  printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
  getch();
 }
2赞 thejosh 2/28/2012 #11

我知道这不是一个快速的解决方案。发布希望更容易理解的慢速解决方案。

 public static long largestPrimeFactor(long n) {

        // largest composite factor must be smaller than sqrt
        long sqrt = (long)Math.ceil(Math.sqrt((double)n));

        long largest = -1;

        for(long i = 2; i <= sqrt; i++) {
            if(n % i == 0) {
                long test = largestPrimeFactor(n/i);
                if(test > largest) {
                    largest = test;
                }
            }
        }

        if(largest != -1) {
            return largest;
        }

        // number is prime
        return n;
    } 
-3赞 pedram 5/17/2013 #12

这是与生成器相同的function@Triptych,也略有简化。

def primes(n):
    d = 2
    while (n > 1):
        while (n%d==0):
            yield d
            n /= d
        d += 1

然后可以使用以下方法找到 MAX Prime:

n= 373764623
max(primes(n))

以及使用以下方法发现的因素列表:

list(primes(n))
-1赞 guoger 11/8/2013 #13

首先计算一个存储质数的列表,例如 2 3 5 7 11 13 ...

每次对一个数进行质因式分解时,请使用 Triptych 的实现,但要迭代这个素数列表而不是自然整数。

0赞 Seamus Barrett 1/20/2014 #14

这是我在 c# 中的尝试。最后的打印输出是数字的最大质因数。我检查了一下,它有效。

namespace Problem_Prime
{
  class Program
  {
    static void Main(string[] args)
    {
      /*
       The prime factors of 13195 are 5, 7, 13 and 29.

      What is the largest prime factor of the number 600851475143 ?
       */
      long x = 600851475143;
      long y = 2;
      while (y < x)
      {
        if (x % y == 0)
        {
          // y is a factor of x, but is it prime
          if (IsPrime(y))
          {
            Console.WriteLine(y);
          }
          x /= y;
        }

        y++;

      }
      Console.WriteLine(y);
      Console.ReadLine();
    }
    static bool IsPrime(long number)
    {
      //check for evenness
      if (number % 2 == 0)
      {
        if (number == 2)
        {
          return true;
        }
        return false;
      }
      //don't need to check past the square root
      long max = (long)Math.Sqrt(number);
      for (int i = 3; i <= max; i += 2)
      {
        if ((number % i) == 0)
        {
          return false;
        }
      }
      return true;
    }

  }
}
-1赞 Paul Vargas 3/29/2014 #15

使用 Java:

对于值:int

public static int[] primeFactors(int value) {
    int[] a = new int[31];
    int i = 0, j;
    int num = value;
    while (num % 2 == 0) {
        a[i++] = 2;
        num /= 2;
    }
    j = 3;
    while (j <= Math.sqrt(num) + 1) {
        if (num % j == 0) {
            a[i++] = j;
            num /= j;
        } else {
            j += 2;
        }
    }
    if (num > 1) {
        a[i++] = num;
    }
    int[] b = Arrays.copyOf(a, i);
    return b;
}

对于值:long

static long[] getFactors(long value) {
    long[] a = new long[63];
    int i = 0;
    long num = value;
    while (num % 2 == 0) {
        a[i++] = 2;
        num /= 2;
    }
    long j = 3;
    while (j <= Math.sqrt(num) + 1) {
        if (num % j == 0) {
            a[i++] = j;
            num /= j;
        } else {
            j += 2;
        }
    }
    if (num > 1) {
        a[i++] = num;
    }
    long[] b = Arrays.copyOf(a, i);
    return b;
}
4赞 the_prole 4/11/2014 #16
    //this method skips unnecessary trial divisions and makes 
    //trial division more feasible for finding large primes

    public static void main(String[] args) 
    {
        long n= 1000000000039L; //this is a large prime number 
        long i = 2L;
        int test = 0;

        while (n > 1)
        {
            while (n % i == 0)
            {
                n /= i;     
            }

            i++;

            if(i*i > n && n > 1) 
            {
                System.out.println(n); //prints n if it's prime
                test = 1;
                break;
            }
        }

        if (test == 0)  
            System.out.println(i-1); //prints n if it's the largest prime factor
    }

评论

1赞 Will Ness 4/12/2014
您是否尝试过使用 1,000,000,000,039 的代码?它也应该在眨眼间运行。是吗?
2赞 Will Ness 4/12/2014
你可以提前知道,不用尝试。10^12 = (2*5)^12 = 2^12 * 5^12。因此,您的循环将遍历 的值。所有 60 次迭代。但是对于 (10^12+39) 将有 (10^12+38) 次迭代,.即使 10^10 次操作需要 1 秒,10^12 次操作也需要 100 秒。但实际上只需要 10^6 次迭代,如果 10^10 次操作需要一秒钟,那么 10^6 次将需要 1/10,000 秒。whilei2,2,2,2,2,2,2,2,2,2,2,2, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5i=2,3,4,5,6,...,10^12+39
1赞 the_prole 4/14/2014
因为我没有意识到 (10^12+39) 是我现在做的质数。我完全明白你在说什么。
1赞 Will Ness 4/14/2014
好的,所以你可以改变你的代码,这样素数就不会有这么大的减速:如果 n = a b 和 a <= b,那么 a a <= b a = n,即 aa <=n。如果我们已经达到 a+1,那么 n 肯定是素数。(如果您编辑您的答案以合并此内容,请与我联系)。
1赞 Will Ness 4/18/2014
当 ?它是否像应有的那样快速工作?(另外,你能通过使用语句来简化你的代码吗?(如果你想让我停止轻推你,就;)说出来)long n = 2*1000000000039Lreturn;
0赞 Rishabh Prasad 6/1/2014 #17
#python implementation
import math
n = 600851475143
i = 2
factors=set([])
while i<math.sqrt(n):
   while n%i==0:
       n=n/i
       factors.add(i)
   i+=1
factors.add(n)
largest=max(factors)
print factors
print largest

评论

1赞 Will Ness 6/1/2014
25 是 25 的最大质因数吗?
0赞 4aRk Kn1gh7 7/12/2014 #18

在 C++ 中使用递归计算数字的最大质因数。代码的工作原理解释如下:

int getLargestPrime(int number) {
    int factor = number; // assumes that the largest prime factor is the number itself
    for (int i = 2; (i*i) <= number; i++) { // iterates to the square root of the number till it finds the first(smallest) factor
        if (number % i == 0) { // checks if the current number(i) is a factor
            factor = max(i, number / i); // stores the larger number among the factors
            break; // breaks the loop on when a factor is found
        }
    }
    if (factor == number) // base case of recursion
        return number;
    return getLargestPrime(factor); // recursively calls itself
}
1赞 Jyothir Aditya Singh 11/9/2015 #19

Python 迭代方法,从数字中删除所有质因数

def primef(n):
    if n <= 3:
        return n
    if n % 2 == 0:
        return primef(n/2)
    elif n % 3 ==0:
        return primef(n/3)
    else:
        for i in range(5, int((n)**0.5) + 1, 6):
            #print i
            if n % i == 0:
                return primef(n/i)
            if n % (i + 2) == 0:
                return primef(n/(i+2))
    return n
0赞 penkovsky 11/22/2015 #20

这是我快速计算最大质因数的方法。 它基于这样一个事实,即修改不包含非质因数。为了实现这一点,一旦找到一个因素,我们就会进行除法。然后,剩下的唯一事情就是返回最大的因子。它已经是黄金了。xx

代码 (Haskell):

f max' x i | i > x = max'
           | x `rem` i == 0 = f i (x `div` i) i  -- Divide x by its factor
           | otherwise = f max' x (i + 1)  -- Check for the next possible factor

g x = f 2 x 2

评论

0赞 Janus Troelsen 2/17/2016
但是,这不会也尝试用所有偶数进行除法吗?
8赞 Vlad Bezden 4/1/2016 #21

JavaScript 代码:

'option strict';

function largestPrimeFactor(val, divisor = 2) { 
    let square = (val) => Math.pow(val, 2);

    while ((val % divisor) != 0 && square(divisor) <= val) {
        divisor++;
    }

    return square(divisor) <= val
        ? largestPrimeFactor(val / divisor, divisor)
        : val;
}

使用示例:

let result = largestPrimeFactor(600851475143);

下面是一个代码示例

0赞 s.n 6/15/2016 #22

以下 C++ 算法不是最好的算法,但它适用于十亿以下的数字,而且速度非常快

#include <iostream>
using namespace std;

// ------ is_prime ------
// Determines if the integer accepted is prime or not
bool is_prime(int n){
    int i,count=0;
    if(n==1 || n==2)
      return true;
    if(n%2==0)
      return false;
    for(i=1;i<=n;i++){
    if(n%i==0)
        count++;
    }
    if(count==2)
      return true;
    else
      return false;
 }
 // ------ nextPrime -------
 // Finds and returns the next prime number
 int nextPrime(int prime){
     bool a = false;
     while (a == false){
         prime++;
         if (is_prime(prime))
            a = true;
     }
  return prime;
 }
 // ----- M A I N ------
 int main(){

      int value = 13195;
      int prime = 2;
      bool done = false;

      while (done == false){
          if (value%prime == 0){
             value = value/prime;
             if (is_prime(value)){
                 done = true;
             }
          } else {
             prime = nextPrime(prime);
          }
      }
        cout << "Largest prime factor: " << value << endl;
 }
1赞 Kalpesh Dusane 9/1/2016 #23

我正在使用的算法继续将数字除以当前的质因数。

我在python 3中的解决方案:

def PrimeFactor(n):
    m = n
    while n%2==0:
        n = n//2
    if n == 1:         # check if only 2 is largest Prime Factor 
        return 2
    i = 3
    sqrt = int(m**(0.5))  # loop till square root of number
    last = 0              # to store last prime Factor i.e. Largest Prime Factor
    while i <= sqrt :
        while n%i == 0:
            n = n//i       # reduce the number by dividing it by it's Prime Factor
            last = i
        i+=2
    if n> last:            # the remaining number(n) is also Factor of number 
        return n
    else:
        return last
print(PrimeFactor(int(input()))) 

输入 : 输出 :105

输入 : 输出 :6008514751436857

9赞 Ugnius Malūkas 2/19/2018 #24

三联画的答案相似,但也不同。在此示例中,不使用列表或字典。代码是用 Ruby 编写的

更新

由于 Cooper Wolfe 的建议,该算法得到了增强和优化。

def largest_prime_factor(number)
  i = 2
  while number > 1
    if number % i == 0
      number /= i
    elsif i > Math.sqrt(number)
      i = number
    else
      i += 1
    end
  end
  return i
end

largest_prime_factor(600851475143)
# => 6857

评论

0赞 Ebuall 4/8/2018
最后,一些可读的东西,同时可以立即(在js中)执行。我试图使用无限素数列表,但它在 100 万上已经太慢了。
0赞 Cooper Wolfe 1/18/2023
另一个 else-if 可以显着减少浪费的计算: else if i > sqrt(number): i = 数字
0赞 greybeard 10/4/2023
(分配是终止循环的一种非常间接的方式 - 我只是 .)i = numberreturn number
0赞 Ugnius Malūkas 10/4/2023
@greybeard是的,但在这种情况下,我更喜欢原样
0赞 Babar-Baig 7/19/2018 #25

“James Wang”在网络上找到了这个解决方案

public static int getLargestPrime( int number) {

    if (number <= 1) return -1;

    for (int i = number - 1; i > 1; i--) {
        if (number % i == 0) {
            number = i;
        }
    }
    return number;
}
0赞 rashedcs 9/13/2018 #26

使用筛子的质因数:

#include <bits/stdc++.h>
using namespace std;
#define N 10001  
typedef long long ll;
bool visit[N];
vector<int> prime;

void sieve()
{
            memset( visit , 0 , sizeof(visit));
            for( int i=2;i<N;i++ )
            {
                if( visit[i] == 0)
                {
                    prime.push_back(i);
                    for( int j=i*2; j<N; j=j+i )
                    {
                        visit[j] = 1;
                    }
                }
            }   
}
void sol(long long n, vector<int>&prime)
{
            ll ans = n;
            for(int i=0; i<prime.size() || prime[i]>n; i++)
            {
                while(n%prime[i]==0)
                {
                    n=n/prime[i];
                    ans = prime[i];
                }
            }
            ans = max(ans, n);
            cout<<ans<<endl;
}
int main() 
{
           ll tc, n;
           sieve();

           cin>>n;
           sol(n, prime);

           return 0;
}
0赞 Vikas Gautam 2/28/2021 #27

这是我在 Clojure 中的尝试。只走几率和素数的素数,即。.使用惰性序列有助于在需要值之前生成值。prime?sieve

(defn prime? 
  ([n]
    (let [oddNums (iterate #(+ % 2) 3)]
    (prime? n (cons 2 oddNums))))
  ([n [i & is]]
    (let [q (quot n i)
          r (mod n i)]
    (cond (< n 2)       false
          (zero? r)     false
          (> (* i i) n) true
          :else         (recur n is)))))

(def primes 
  (let [oddNums (iterate #(+ % 2) 3)]
  (lazy-seq (cons 2 (filter prime? oddNums)))))

;; Sieve of Eratosthenes
(defn sieve
  ([n] 
    (sieve primes n))
  ([[i & is :as ps] n]
    (let [q (quot n i)
          r (mod n i)]
    (cond (< n 2)       nil
          (zero? r)     (lazy-seq (cons i (sieve ps q)))
          (> (* i i) n) (when (> n 1) (lazy-seq [n]))
          :else         (recur is n)))))

(defn max-prime-factor [n]
  (last (sieve n)))
0赞 Sam Ginrich 4/4/2021 #28

猜猜看,除了执行因式分解之外,没有直接的方法,就像上面的例子一样,即

在迭代中,您确定数字 N 的“小”因子 f,然后继续简化问题“找到 N':=N/f 的最大质因数,候选因子为 >=f”。

从一定大小的 f 开始,如果你对约简的 N' 进行质数检验,预期的搜索时间会更短,这证实了你的 N' 已经是初始 N 的最大质因数。

2赞 Arty 8/31/2021 #29

第 1 部分。Pollard-Rho + 审判部门。

受到您的问题的启发,我决定在 Python 中实现我自己的因式分解版本(并找到最大质因数)。

据我所知,最容易实现但非常有效的分解算法可能是 Pollard 的 Rho 算法。它的运行时间最多比试除算法的运行时间快得多。这两种算法仅在复合数(非素数)的情况下才有这些运行时间,这就是为什么应该使用素数检验来过滤掉素数(不可因式)数的原因。O(N^(1/4))O(N^(1/2))

我在代码中使用了以下算法:费马素数测试...,Pollard的Rho算法...,Trial Division算法。在运行 Pollard 的 Rho 之前使用费马素数检验,以过滤掉素数。Trial Division 被用作后备,因为在极少数情况下,Pollard 的 Rho 可能无法找到因子,尤其是对于一些小数字。

显然,在将一个数字完全分解到质因数排序列表中后,最大的质因数将是该列表中的最后一个元素。在一般情况下(对于任何随机数),除了完全分解一个数字之外,我不知道任何其他方法可以找出最大的质因数。

例如,在我的代码中,我以 Pi 的前 190 个小数位分解,代码在 1 秒内分解这个数字,并显示最大的质因数,大小为 165 位(545 位)!

在线试用!

def is_fermat_probable_prime(n, *, trials = 32):
    # https://en.wikipedia.org/wiki/Fermat_primality_test
    import random
    if n <= 16:
        return n in (2, 3, 5, 7, 11, 13)
    for i in range(trials):
        if pow(random.randint(2, n - 2), n - 1, n) != 1:
            return False
    return True

def pollard_rho_factor(N, *, trials = 16):
    # https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
    import random, math
    for j in range(trials):
        i, stage, y, x = 0, 2, 1, random.randint(1, N - 2)
        while True:
            r = math.gcd(N, x - y)
            if r != 1:
                break
            if i == stage:
                y = x
                stage <<= 1
            x = (x * x + 1) % N
            i += 1
        if r != N:
            return [r, N // r]
    return [N] # Pollard-Rho failed

def trial_division_factor(n, *, limit = None):
    # https://en.wikipedia.org/wiki/Trial_division
    fs = []
    while n & 1 == 0:
        fs.append(2)
        n >>= 1
    d = 3
    while d * d <= n and limit is None or d <= limit:
        q, r = divmod(n, d)
        if r == 0:
            fs.append(d)
            n = q
        else:
            d += 2
    if n > 1:
        fs.append(n)
    return fs

def factor(n):
    if n <= 1:
        return []
    if is_fermat_probable_prime(n):
        return [n]
    fs = trial_division_factor(n, limit = 1 << 12)
    if len(fs) >= 2:
        return sorted(fs[:-1] + factor(fs[-1]))
    fs = pollard_rho_factor(n)
    if len(fs) >= 2:
        return sorted([e1 for e0 in fs for e1 in factor(e0)])
    return trial_division_factor(n)

def demo():
    import time, math
    # http://www.math.com/tables/constants/pi.htm
    # pi = 3.
    #     1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679
    #     8214808651 3282306647 0938446095 5058223172 5359408128 4811174502 8410270193 8521105559 6446229489 5493038196
    #     4428810975 6659334461 2847564823 3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273
    #     7245870066 0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951 9415116094
    #     3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735 1885752724 8912279381 8301194912
    #     9833673362 4406566430 8602139494 6395224737 1907021798 6094370277 0539217176 2931767523 8467481846 7669405132
    #
    # n = first 190 fractional digits of Pi
    n =   1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679_8214808651_3282306647_0938446095_5058223172_5359408128_4811174502_8410270193_8521105559_6446229489
    print('Number:', n)
    tb = time.time()
    fs = factor(n)
    print('All Prime Factors:', fs)
    print('Largest Prime Factor:', f'({math.log2(fs[-1]):.02f} bits, {len(str(fs[-1]))} digits)', fs[-1])
    print('Time Elapsed:', round(time.time() - tb, 3), 'sec')

if __name__ == '__main__':
    demo()

输出:

Number: 1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489
All Prime Factors: [3, 71, 1063541, 153422959, 332958319, 122356390229851897378935483485536580757336676443481705501726535578690975860555141829117483263572548187951860901335596150415443615382488933330968669408906073630300473]
Largest Prime Factor: (545.09 bits, 165 digits) 122356390229851897378935483485536580757336676443481705501726535578690975860555141829117483263572548187951860901335596150415443615382488933330968669408906073630300473
Time Elapsed: 0.593 sec

第 2 部分。椭圆曲线法。

一段时间后,我决定通过从头开始实现更高级的椭圆曲线分解方法(称为 ECM)来改进我的帖子,请参阅维基百科文章 Lenstra 椭圆曲线分解

这种方法比我的回答帖子的第 1 部分中描述的 Pollard Rho 要快得多。椭圆 ECM 方法的时间复杂度为 ,其中表示一个数的最小质因数。而 Pollard Rho 的时间复杂度是 .因此,对于足够大的最小 P 因子,ECM 要快得多。O(exp[(Sqrt(2) + o(1)) Sqrt(ln p ln ln p)])pO(Sqrt(p))

ECM方法的步骤:

  1. 检查数字是否小于 2^16,然后通过试除法进行因式分解。返回结果。

  2. 检查数字是否可能是具有高条件的素数,为此我使用费马测试进行 32 次试验。要克服卡迈克尔数,您可以改用米勒拉宾测试。如果 number 是质数,则将其作为唯一的因数返回。

  3. 随机生成曲线参数,由曲线方程推导。检查曲线是否正常,值应为非零。A, X, YBY^2 = X^3 + AX + B (mod N)4 * A ** 3 - 27 * B ** 2

  4. 通过埃拉托色尼的筛子产生小素数,素数低于我们的边界。每个素数提高到一些小功率,这个提高的素数将被称为 K。我确实在它比某些 Bound2 小的时候提升了力量,就我而言。Sqrt(Bound)

  5. 计算椭圆点乘法,其中 K 取自上一步,P 为 (X, Y)。根据 Wiki 进行计算。P = k * P

  6. 点乘法使用模块化逆法,根据 Wiki 进行计算。如果此 GCD 不为 1,则它给出非 1 的 N 因子,因此在本例中,I 通过 Exception 并从 ECM 分解算法返回该因子。GCD(SomeValue, N)

  7. 如果直到 Bound 的所有素数都相乘并且没有给出任何因子,则使用另一条随机曲线和更大的 Bound 再次重新运行 ECM 因式分解算法(如上所述)。在我的代码中,我通过将 256 添加到旧边界来获取新边界。1.-6.

我的 ECM 代码中使用了以下子算法,所有提到的子算法都链接到相应的维基百科文章:试除因式分解费马概率测试埃拉托色尼筛(素数生成器)、欧几里得算法(计算最大公约数,GCD)、扩展欧几里得算法(具有贝祖系数的 GCD)、模乘法逆从右到左的二进制指数 (用于椭圆点乘法)、椭圆曲线算术(点加法和乘法)、Lenstra 椭圆曲线因式分解

与我的答案的第 1 部分不同,我在 Pi 数的位数中分解,在第 2 部分中,我创建了一个由 组成的特殊数,这意味着数字是 24 位和 35 位的随机素以及 37 等的乘积......此自定义数字在视觉上更令人印象深刻,以显示算法功能。n = Prime(24) * Prime(35) * Prime(37) * ... etc

与我答案的第 1 部分一样,我还使用几种方法来比较速度,并用更简单的方法分解出较小的因素。因此,在下面的代码中,我使用 Trial Division + Pollard Rho + 椭圆曲线方法。

在下面的代码之后,请参阅控制台输出以找出我的代码输出的内容。

在线试用!

def is_fermat_probable_prime(n, *, trials = 32):
    # https://en.wikipedia.org/wiki/Fermat_primality_test
    import random
    if n <= 16:
        return n in (2, 3, 5, 7, 11, 13)
    for i in range(trials):
        if pow(random.randint(2, n - 2), n - 1, n) != 1:
            return False
    return True

def pollard_rho_factor(N, *, trials = 8, limit = None):
    # https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
    import random, math
    if N <= 1:
        return []
    if is_fermat_probable_prime(N):
        return [N]
    for j in range(trials):
        i, stage, y, x = 0, 2, 1, random.randint(1, N - 2)
        while True:
            r = math.gcd(N, x - y)
            if r != 1:
                break
            if i == stage:
                y = x
                stage <<= 1
            x = (x * x + 1) % N
            i += 1
            if limit is not None and i >= limit:
                return [N] # Pollard-Rho failed
        if r != N:
            return sorted(pollard_rho_factor(r, trials = trials, limit = limit) +
                pollard_rho_factor(N // r, trials = trials, limit = limit))
    return [N] # Pollard-Rho failed

def trial_division_factor(n, *, limit = None):
    # https://en.wikipedia.org/wiki/Trial_division
    fs = []
    while n & 1 == 0:
        fs.append(2)
        n >>= 1
    d = 3
    while d * d <= n and limit is None or d <= limit:
        q, r = divmod(n, d)
        if r == 0:
            fs.append(d)
            n = q
        else:
            d += 2
    if n > 1:
        fs.append(n)
    return fs
    
def ecm_factor(N0, *, verbose = False):
    # https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization
    import math, random, time; gmpy2 = None
    #import gmpy2
    def GenPrimes_SieveOfEratosthenes(end):
        # https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
        composite = [False] * (end // 2) # Allocate for odd numbers only
        for p in range(3, int(end ** 0.5 + 3), 2):
            if composite[p >> 1]:
                continue
            for i in range(p * p, end, p * 2):
                composite[i >> 1] = True
        yield 2
        for p in range(3, end, 2):
            if not composite[p >> 1]:
                yield p
    def GCD(a, b):
        # https://en.wikipedia.org/wiki/Euclidean_algorithm
        # return math.gcd(a, b)
        while b != 0:
            a, b = b, a % b
        return a
    def EGCD(a, b):
        # https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
        if gmpy2 is None:
            ro, r, so, s = a, b, 1, 0
            while r != 0:
                ro, (q, r) = r, divmod(ro, r)
                so, s = s, so - q * s
            return ro, so, (ro - so * a) // b
        else:
            return tuple(map(int, gmpy2.gcdext(a, b)))
    def ModularInverse(a, n):
        # https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
        # return pow(a, -1, n)
        g, s, t = EGCD(a, n)
        if g != 1:
            raise ValueError(a)
        return s % n
    def EllipticCurveAdd(N, A, B, X0, Y0, X1, Y1):
        # https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication
        if X0 == X1 and Y0 == Y1:
            # Double
            l = ((3 * X0 ** 2 + A) * ModularInverse(2 * Y0, N)) % N
            x = (l ** 2 - 2 * X0) % N
            y = (l * (X0 - x) - Y0) % N
        else:
            # Add
            l = ((Y1 - Y0) * ModularInverse(X1 - X0, N)) % N
            x = (l ** 2 - X0 - X1) % N
            y = (l * (X0 - x) - Y0) % N
        return x, y
    def EllipticCurveMul(N, A, B, X, Y, k):
        # https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
        assert k >= 2, k
        k -= 1
        BX, BY = X, Y
        while k != 0:
            if k & 1:
                X, Y = EllipticCurveAdd(N, A, B, X, Y, BX, BY)
            BX, BY = EllipticCurveAdd(N, A, B, BX, BY, BX, BY)
            k >>= 1
        return X, Y
    
    bound_start = 1 << 9
    
    def Main(N, *, bound = bound_start, icurve = 0):
        def NextFactorECM(x):
            return Main(x, bound = bound + bound_start, icurve = icurve + 1)
        def PrimePow(p, *, bound2 = int(bound ** 0.5 + 1.01)):
            mp = p
            while True:
                mp *= p
                if mp >= bound2:
                    return mp // p
        
        if N <= 1:
            return []
            
        if N < (1 << 16):
            fs = trial_division_factor(N)
            if verbose and len(fs) >= 2:
                print('Factors from TrialDiv:', fs, flush = True)
            return fs
            
        if is_fermat_probable_prime(N):
            return [N]
        
        if verbose:
            print(f'Curve {icurve:>4},  bound 2^{math.log2(bound):>7.3f}', flush = True)
        
        while True:
            X, Y, A = [random.randrange(N) for i in range(3)]
            B = (Y ** 2 - X ** 3 - A * X) % N
            if 4 * A ** 3 - 27 * B ** 2 == 0:
                continue
            break
        
        for p in GenPrimes_SieveOfEratosthenes(bound):
            k = PrimePow(p)
            try:
                X, Y = EllipticCurveMul(N, A, B, X, Y, k)
            except ValueError as ex:
                g = GCD(ex.args[0], N)
                assert g > 1, g
                if g != N:
                    if verbose:
                        print(f'Factor from ECM: {g} ({math.log2(g):.1f} bits)', flush = True)
                    return sorted(NextFactorECM(g) + NextFactorECM(N // g))
                else:
                    return NextFactorECM(N)
        
        return NextFactorECM(N)
    
    return Main(N0)

def factor(n):
    if n <= 1:
        return []
    if is_fermat_probable_prime(n):
        return [n]
    fs1 = trial_division_factor(n, limit = 1 << 12)
    fs1, n2 = fs1[:-1], fs1[-1]
    print(len(fs1), 'factors from TrialDivision')
    fs2 = pollard_rho_factor(n2, limit = 1 << 17)
    fs2, n3 = fs2[:-1], fs2[-1]
    print(len(fs2), 'factors from PollardRho')
    fs3 = ecm_factor(n3, verbose = True)
    print(len(fs3), 'factors from ECM')
    return sorted(fs1 + fs2 + fs3)

def demo():
    import time, math, random
    def Prime(bits):
        while True:
            x = random.randrange(1 << (bits - 1), 1 << bits)
            if is_fermat_probable_prime(x):
                return x
    
    # http://www.math.com/tables/constants/pi.htm
    # pi = 3.
    #     1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679
    #     8214808651 3282306647 0938446095 5058223172 5359408128 4811174502 8410270193 8521105559 6446229489 5493038196
    #     4428810975 6659334461 2847564823 3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273
    #     7245870066 0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951 9415116094
    #     3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735 1885752724 8912279381 8301194912
    #     9833673362 4406566430 8602139494 6395224737 1907021798 6094370277 0539217176 2931767523 8467481846 7669405132
    #
    # n =   1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679_8214808651_3282306647_0938446095_5058223172_5359408128_4811174502_8410270193_8521105559_6446229489_5493038196_4428810975_6659334461_2847564823_3786783165_2712019091_4564856692_3460348610_4543266482_1339360726_0249141273_7245870066_0631558817_4881520920_9628292540_9171536436_7892590360_0113305305_4882046652_1384146951_9415116094_3305727036_5759591953_0921861173_8193261179_3105118548_0744623799_6274956735_1885752724_8912279381_8301194912_9833673362_4406566430_8602139494_6395224737_1907021798_6094370277_0539217176_2931767523_8467481846_7669405132
    #       141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132
    
    n = Prime(9) * Prime(10) * Prime(11) * Prime(20) * Prime(24) * Prime(35) * Prime(37) * Prime(38) * Prime(120)
    print('Number:', n)
    tb = time.time()
    fs = factor(n)
    print('All Prime Factors:', fs)
    print('Largest Prime Factor:', f'({math.log2(fs[-1]):.02f} bits, {len(str(fs[-1]))} digits)', fs[-1])
    if len(fs) >= 2:
        print('2nd-largest Prime Factor:', f'({math.log2(fs[-2]):.02f} bits, {len(str(fs[-2]))} digits)', fs[-2])
    print('Time Elapsed:', round(time.time() - tb, 3), 'sec')

if __name__ == '__main__':
    demo()

控制台输出:

Number: 3020823358956369790763854998578637168366763837218991014777892420353187988302225517459334041

3 factors from TrialDivision
2 factors from PollardRho

Curve    0,  bound 2^  9.000
Curve    1,  bound 2^ 10.000
Curve    2,  bound 2^ 10.585
Factor from ECM: 20028139561 (34.2 bits)
Curve    3,  bound 2^ 11.000
Curve    4,  bound 2^ 11.322
Curve    5,  bound 2^ 11.585
Curve    6,  bound 2^ 11.807
Curve    7,  bound 2^ 12.000
Curve    8,  bound 2^ 12.170
Factor from ECM: 96583780901 (36.5 bits)
Curve    9,  bound 2^ 12.322
Curve   10,  bound 2^ 12.459
Curve   11,  bound 2^ 12.585
Curve   12,  bound 2^ 12.700
Curve   13,  bound 2^ 12.807
Curve   14,  bound 2^ 12.907
Curve   15,  bound 2^ 13.000
Curve   16,  bound 2^ 13.087
Curve   17,  bound 2^ 13.170
Factor from ECM: 239171423261 (37.8 bits)
4 factors from ECM

All Prime Factors: [397, 1021, 1459, 754333, 16156687, 20028139561,
    96583780901, 239171423261, 905908369146483365552973334921981697]
Largest Prime Factor: (119.45 bits, 36 digits) 905908369146483365552973334921981697
2nd-largest Prime Factor: (37.80 bits, 12 digits) 239171423261

Time Elapsed: 17.156 sec
0赞 4d30 7/31/2022 #30

C 语言中的递归

算法可以是

  1. 检查 n 是因子还是 t
  2. 检查 n 是否为素数。如果是这样,请记住 n
  3. 递增 n
  4. 重复直到 n > sqrt(t)

下面是 C 语言中问题的(尾部)递归解决方案的示例:

#include <stdio.h>
#include <stdbool.h>

bool is_factor(long int t, long int n){
    return ( t%n == 0);
}

bool is_prime(long int n0, long int n1, bool acc){
    if ( n1 * n1 > n0 || acc < 1 )
        return acc;
    else
        return is_prime(n0, n1+2, acc && (n0%n1 != 0));
}

int gpf(long int t, long int n, long int acc){
    if (n * n > t)
        return acc;
    if (is_factor(t, n)){
        if (is_prime(n, 3, true))
            return gpf(t, n+2, n);
        else
            return gpf(t, n+2, acc);
    }
    else
        return gpf(t, n+2, acc);
}

int main(int argc, char ** argv){
    printf("%d\n", gpf(600851475143, 3, 0));
    return 0;
}

该解决方案由三个功能组成。一个用于测试候选因子是否为一个因子,另一个用于测试该因子是否为素数,最后一个用于将这两者组合在一起。

以下是一些关键思想:

1- 在 sqrt(600851475143) 处停止递归

2- 仅测试因数的奇数

3- 仅测试奇数质数的候选因子