查找数的最大素数的算法


183

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

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

  1. 查找可以清楚地划分的最低质数
  2. 检查除法结果是否为质数
  3. 如果没有,找到下一个最低的
  4. 转到2。

我基于此假设,因为它更容易计算出小的素因数。这是对的吗?我还应该考虑其他什么方法?

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

再次编辑:现在我意识到它仍然可以工作,因为最后找到的质数必须是最高的质数,因此,对步骤2中非质数结果进行的任何进一步测试都将导致较小的质数。


我的方法是:(1)将可能的大数除以2;(2)检查是否大量均分;(3)如果是,请检查2除数是否为质数。如果是,请返回。(4)否则,1。减去从由2号分割,返回到步骤3。
凯文梅雷迪思

1.找到明确除以(对于i = 2到int(sqr(num))的任何数字)2.除以该数字(num = num / i)并重复出现,直到在1中找不到任何值为止。间隔3. num是最大因素
user3819867

1
我们可以除以小素数,最后剩下的是最大素数(我想)

Answers:


134

实际上,有几种更有效的方法来查找大数的因子(对于较小的数,审判分庭相当有效)。

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

二次筛是最著名的分解100位数字的方法。另外,算法的一部分很容易通过并行处理完成。

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


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

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


3
与二次筛相比,Pollard rho和椭圆曲线方法在消除数量小的素数方面要好得多。无论数量多少,QS的运行时间都差不多。哪种方法更快,取决于您的电话号码。QS可以更快地破解难以分解的数,而rho和ECM可以更快地分解易于分解的数。
tmyklebu

感谢您提出二次方变化建议。我需要为我的一位客户实现这一点,我提出的最初版本与@mercutio在他的问题中建议的类似。二次方解决方案现在可以通过math.tools/calculator/prime-factors为我的客户的工具提供动力
dors

如果有解决此问题的有效方法,这是否意味着Web加密不安全?
BKSpurgeon

141

这是我所知道的最佳算法(在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
请在投票之前阅读和/或运行此代码。它工作正常。只需复制并粘贴。如上所写,prime_factors(1000)将返回[2,2,2,5,5,5],应将其解释为2 ^ 3 * 5 ^ 3,又称素数分解。
三联画

11
“在O(sqrt(n))最坏的情况下运行”-不,它在O(n)最坏的情况下运行(例如,当n黄金时段)
Sheldon L. Cooper 2010年

16
容易将其设为O(sqrt(n)),只需在d * d> n时停止循环,如果此时n> 1,则应将其值附加到素因数列表中。
Sumudu Fernando 2012年

5
有这个名字吗?
Forethinker

11
因为2是唯一的偶数质数,所以不必每次都加1,而是可以分别对d = 2进行迭代,然后将其递增1,然后从d = 3开始,可以递增2。因此,它将减少数量的迭代... :)
裁缝_raj

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

我最近写了一篇博客文章,解释了该算法的工作原理。

我敢冒险,一种不需要测试原始性(也不需要筛子构造)的方法会比使用那些方法的方法运行得更快。如果真是这样,这可能是最快的算法。


12
您实际上可以更进一步地理解这个想法,例如,超过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
Tobias Kienzler

2
我猜应该是while (multOfSix - 1 <= n)
Nader Ghanbari 2014年

8

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

这是代码示例


7

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

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

largest_prime_factor(600851475143)
# => 6857

最终,某些东西同时可读且可立即执行(在js中)。我正在尝试使用无限质数列表,但是在100万个列表上已经太慢了。
Ebuall

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

是的,但是效率很低。一旦将所有2均分,您就不应该尝试除以4或6或...;。仅检查质数或使用某些toher算法,实际上在限制方面要高效得多。
08年

6
+1抵消噪声,我认为这是错误的。尝试除以4只会发生一次,并且会立即失败。我认为这并不比从某些候选者列表中删除4个更糟糕,而且肯定比预先找到所有素数更快。
三联画

2
@Beowulf。在投票否决之前,尝试运行此代码。它返回主要因素;您只是不了解该算法。
三联画

3
该代码可以正常工作,但是如果传入的数字是素数,则代码运行缓慢。我也只会跑到平方并加2。但是对于很大的数字来说可能太慢了。
blabla999

4
blabla999完全正确。示例是1234567898766700 = 2 * 2 * 5 * 5 * 12345678987667。到达时currFactor = 3513642,我们知道12345678987667是质数,应将其返回为答案。而是,此代码将继续枚举,直到12345678987667为止。这比必要的速度慢了3,513,642倍。
Will Ness 2014年

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
    }

1
您是否以1,000,000,000,039尝试了您的代码?它也应该在眨眼间运行。可以?
Will Ness 2014年

2
您可以事先知道它,而无需尝试。10 ^ 12 =(2 * 5)^ 12 = 2 ^ 12 * 5 ^ 12。因此,您的while循环将经过的i2,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,5。全部60次迭代。但是对于(10 ^ 12 + 39),将有(10 ^ 12 + 38)次迭代i=2,3,4,5,6,...,10^12+39。即使10 ^ 10操作花费一秒钟,10 ^ 12也将花费100秒。但是实际上只需要10 ^ 6迭代,如果10 ^ 10 op花费一秒钟,那么10 ^ 6将花费1 / 10,000秒。
Will Ness 2014年

1
因为我没有意识到(10 ^ 12 + 39)是我现在做的质数。我完全明白你的意思。
the_prole

1
好的,因此您可以更改代码,以免质数的下降如此之快:如果n = a b和a <= b,则 a <= b a = n,即a a <= n 。如果我们达到a + 1,那么n肯定是质数。(如果您编辑答案以包含此内容,请对我进行ping操作)。
Will Ness 2014年

1
什么时候会发生long n = 2*1000000000039L?它能以应有的速度运行吗?(同样,您可以使用return;语句来简化代码吗?)。(如果您想让我停止轻描淡写,请说出这样;))
内斯(Ness

4

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

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

  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,但是算法保持不变。


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)的i的所有倍数都标记为不是素数,而使用while循环。
  • 将i设置为列表中的下一个素数。

也看到这个问题


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

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

1

我正在使用将数字除以当前素数的算法。

我在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()))) 

输入:10 输出:5

输入:600851475143 输出:6857


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

  }
}

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

1
25的最大素数是25吗?
尼斯

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
}

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

但这也不会尝试除以所有偶数吗?
Janus Troelsen

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

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

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

-1

在我看来,给出的算法的第2步并不会是那么有效的方法。您没有合理的期望,这是首要的。

同样,先前的答案暗示着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不能。筛选器需要执行很多操作才能剔除所有合成数字。

另外,请注意,我在确定因素时将其分开会减少必须测试的空间。


那就是我说的,但是被否决了:(我想问题是,如果数字有很大的素数(例如它本身),那么此方法必须一直循环到那个数字。在很多情况下但是,这种方法非常有效
nickf

仔细阅读您的内容是一样的,但是您的第一部分令人困惑。
罗兰·佩希特尔

尝试使用此号码143816789988504044536402352738195137137863656439,让我知道这有多有效……
MichaelICE,2009年

-1

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

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


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

-2

这可能并不总是更快,但是对于找到一个主要的除数,我们会更加乐观:

  1. N 是你的电话号码
  2. 如果是素数 return(N)
  3. 计算素数直到 Sqrt(N)
  4. 以降序(最大的先后)遍历素数
    • 如果那样的N is divisible by PrimeReturn(Prime)

编辑:在第3步中,您可以使用Eratosthenes筛子或Atkins筛子或任何您喜欢的筛子,但是筛子本身不会找到最大的主要因素。(这就是为什么我不选择SQLMenace的帖子作为正式答案的原因...)


1
您是否不需要进行试验分解以确定它是否是质数(步骤2)?另外,考虑找到最大的素数因子15。最多sqrt(15)的素数为2和3;最大素数为2和3。但是最大的素数是5,不是吗?同样用20
乔纳森·莱弗勒

-3

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

当然,我认为您的电话号码不是太大:)


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

这不是问题的答案。;-)问题在于寻找最大的素因,而不是检查素数。
汉斯·彼得·斯托尔2009年

将循环初始化为(long i = 3; i <checkUpTo; i + = 2)
cjk,2009年

-3

这是与生成器提供的功能相同的三叉戟,也略有简化。

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

-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();
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.