查找数字的最大素数因子的算法

时间:2008-08-22 19:35:51

标签: algorithm math prime-factoring

计算数字中最大素因子的最佳方法是什么?

我认为效率最高的是:

  1. 查找干净利落的最低素数
  2. 检查分割结果是否为素数
  3. 如果没有,请找到下一个最低
  4. 转到2.
  5. 我基于这个假设,因为它更容易计算小素因子。这是对的吗?我应该研究哪些其他方法?

    编辑:我现在意识到,如果有超过2个素数因素,我的方法是徒劳的,因为当结果是两个其他素数的乘积时,步骤2失败,因此需要递归算法。

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

29 个答案:

答案 0 :(得分:136)

这是我所知道的最好的算法(在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

答案 1 :(得分:132)

实际上,有几种更有效的方法可以找到大数的因素(对于较小的因子,试验分工合理地工作得很好)。

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

将最长100位数的因子分解的最有名方法是Quadratic sieve。作为奖励,部分算法可以通过并行处理轻松完成。

我听说的另一种算法是Pollard's Rho algorithm。它一般不如Quadratic Sieve有效,但似乎更容易实现。


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

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

答案 2 :(得分:18)

我的答案基于Triptych,但在其上有很多改进。它基于超过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;
}

我最近写了一篇blog article来解释这个算法的工作原理。

我敢说,一种不需要进行素数测试(并且没有筛子构造)的方法比使用那些方法的方法运行得更快。如果是这种情况,这可能是这里最快的算法。

答案 3 :(得分:7)

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);

Here is an example of the code

答案 4 :(得分:4)

    //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
    }

答案 5 :(得分:4)

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

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

  1. 从大于1的所有自然数列表开始。
  2. 删除所有不是素数的数字。也就是说,没有素数因素的数字(除了他们自己)。见下文。
  3. 第二个函数按递增顺序返回给定数字n的素因子。

    1. 列出所有素数(见上文)。
    2. 删除所有非n因子的数字。
    3. 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的素数和/或因子,但算法保持不变。

答案 6 :(得分:4)

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

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.
}

答案 7 :(得分:4)

类似@Triptych的答案也有所不同。在此示例中,不使用列表或字典。代码用Ruby编写

def largest_prime_factor(number)
  i = 2
  while number > 1
    if number % i == 0
      number /= i;
      i -= 1
    end
    i += 1
  end
  return i
end

largest_prime_factor(600851475143)
# => 6857

答案 8 :(得分:2)

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的素数,这里有几个其他答案。

你实际上可以在这里交织Eratosthenes的筛子:

  • 首先创建整数列表 到sqrt(n)。
  • 在for循环中标记所有倍数 我不知道新的sqrt(n) 素数,而是使用while循环。
  • 将我设为下一个素数 清单。

另见this question

答案 9 :(得分:2)

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

 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;
    } 

答案 10 :(得分:1)

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

答案 11 :(得分:1)

I am using algorithm which continues dividing the number by it's current Prime Factor.

My Solution in 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()))) 

Input : 10 Output : 5

Input : 600851475143 Output : 6857

答案 12 :(得分:0)

由“ 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;
}

答案 13 :(得分:0)

使用筛子的底料:

#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;
}

答案 14 :(得分:0)

以下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;
 }

答案 15 :(得分:0)

这是我在 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)))

答案 16 :(得分:0)

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

在迭代中,您确定一个数 N 的“小”因子 f,然后继续简化问题“找到 N' 的最大素因子” :=N/f 带有候选因子 >=f ".

f 的特定大小开始,如果您对减少的 N' 进行素性测试,则预期的搜索时间会更短,以防万一,您的 N' 已经是初始 N 的最大质因数。

答案 17 :(得分:0)

这是我快速计算最大素因子的方法。 它基于以下事实:修改后的x不包含非素因子。为实现这一目标,我们会在找到因素时立即划分x。然后,唯一剩下的就是返回最大因子。它已经是素数了。

代码(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

答案 18 :(得分:0)

这是我在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;
    }

  }
}

答案 19 :(得分:0)

使用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
}

答案 20 :(得分:0)

#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

答案 21 :(得分:-1)

使用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;
}

答案 22 :(得分:-1)

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

每当你对一个数字进行分解时,使用Triptych的实现,但是迭代这个素数列表而不是自然整数。

答案 23 :(得分:-1)

在我看来,所给出的算法的第二步并不是那种有效的方法。你没有合理的期望它是最好的。

此外,之前的答案暗示了Eratosthenes的筛子是完全错误的。我刚写了两个程序来考虑123456789.一个基于Sieve,一个是基于以下内容:

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使用大量操作来识别所有复合数字。

另请注意,我识别出的因素会减少必须测试的空间。

答案 24 :(得分:-2)

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

  1. N是您的号码
  2. 如果是素数那么return(N)
  3. 计算素数直到Sqrt(N)
  4. 按降序排列素数(最大的第一个)
    • 如果N is divisible by PrimeReturn(Prime)
  5. 编辑:在第3步中,您可以使用Eratosthenes的筛子或阿特金斯的筛子或任何您喜欢的筛子,但筛子本身并不会找到最重要的因素。 (这就是为什么我不选择SQLMenace的帖子作为正式答案......)

答案 25 :(得分:-3)

我认为将所有可能的素数存储在小于n的位置并且只是遍历它们以找到最大的分数是很好的。你可以从prime-numbers.org获得素数。

当然我认为你的号码不是太大了:)

答案 26 :(得分:-3)

这是@ Triptych作为发生器提供的相同功能,它也略有简化。

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

然后可以使用以下命令找到最大素数:

n= 373764623
max(primes(n))

以及使用以下内容找到的因素列表:

list(primes(n))

答案 27 :(得分:-3)

不是最快但有效!

    static bool IsPrime(long num)
    {
        long checkUpTo = (long)Math.Ceiling(Math.Sqrt(num));
        for (long i = 2; i <= checkUpTo; i++)
        {
            if (num % i == 0)
                return false;
        }
        return true;
    }

答案 28 :(得分:-6)

#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();
 }