【问题标题】:Prime Factorization Program in JavaJava中的素数分解程序
【发布时间】:2011-05-15 11:23:05
【问题描述】:

我正在研究一个用 Java 实现的素数分解程序。 目标是找到 600851475143 (Project Euler problem 3) 的最大素数。 我想我已经完成了大部分工作,但我遇到了一些错误。 此外,我的逻辑似乎不正确,特别是我设置的用于检查数字是否为素数的方法。

public class PrimeFactor {

    public static void main(String[] args) {
        int count = 0;
        for (int i = 0; i < Math.sqrt(600851475143L); i++) {
            if (Prime(i) && i % Math.sqrt(600851475143L) == 0) {
                count = i;
                System.out.println(count);
            }
        }
    }

    public static boolean Prime(int n) {
        boolean isPrime = false;
        // A number is prime iff it is divisible by 1 and itself only
        if (n % n == 0 && n % 1 == 0) {
            isPrime = true;
        }
        return isPrime;
    }
}

编辑

public class PrimeFactor {

    public static void main(String[] args) {
        for (int i = 2; i <= 600851475143L; i++) {
            if (isPrime(i) == true) {
                System.out.println(i);
            }
        }
    }

    public static boolean isPrime(int number) {
        if (number == 1) return false;
        if (number == 2) return true;
        if (number % 2 == 0) return false;
        for (int i = 3; i <= number; i++) {
            if (number % i == 0) return false;
        }
        return true;
    }
}

【问题讨论】:

  • 您遇到什么错误?你在哪一行得到错误?
  • 您的 Prime 方法总是返回 true,因为 n%n == 0 &amp;&amp; n%1 == 0 代表所有 n。也就是说,所有数字都可以被自己和 1 整除。您缺少定义的“唯一”部分。
  • 不幸的是,你甚至没有接近。您的素数算法不起作用,因为所有数字都可以被自己和零整除 - 只是素数不能被其他任何东西整除,您必须对此进行检查。 Erasthones 筛需要 600GB 的 RAM 才能运行到 600B 范围内的值,因此递归素数分解是唯一实用的策略,而且问题空间很大,需要数小时或数天。这是所有现代加密的基础:大于 RAM 大小的素数分解非常慢。
  • 是的,这是逻辑错误之一,我该如何解决?
  • @Adam,我认为您的意思是“可被自己整除并且 一个 ”。

标签: java math primes


【解决方案1】:

为什么要这么复杂?您不需要执行 isPrime() 之类的操作。除以它的最小除数(素数)并从这个素数开始循环。这是我的简单代码:

public class PrimeFactor {

    public static int largestPrimeFactor(long number) {
        int i;

        for (i = 2; i <= number; i++) {
            if (number % i == 0) {
                number /= i;
                i--;
            }
        }

        return i;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(largestPrimeFactor(13195));
        System.out.println(largestPrimeFactor(600851475143L));
    }
}

【讨论】:

  • 提示:大于 2147483647 (Integer.MAX_VALUE) 的大质数是怎么回事?在这种情况下,也许你应该使用 long 作为变量“i”的类型。
  • @kisp 我们为什么要做i--
  • 你在测试我吗? ;) 这个循环从最小的顺序开始。一个数字可以被同一个数多次潜水,想象一下 2 * 2 * 2 * 2
【解决方案2】:

编辑:我希望这听起来不会令人难以置信的居高临下。我只是想说明,从计算机的角度来看,你必须检查所有可能成为 X 因数的数字,以确保它是素数。计算机不会知道仅通过查看它是复合的,因此您必须迭代

示例:X 是质数吗?

对于 X = 67 的情况:

你如何检查这个?

I divide it by 2... it has a remainder of 1 (this also tells us that 67 is an odd number)
I divide it by 3... it has a remainder of 1
I divide it by 4... it has a remainder of 3
I divide it by 5... it has a remainder of 2
I divide it by 6... it has a remainder of 1

事实上,如果数字不是素数,你只会得到余数 0。

您是否必须检查每个小于 X 的数字以确保它是素数?没有。不用了,感谢数学(!)

让我们看一个较小的数字,比如 16。

16 不是素数。

为什么?因为

2*8 = 16
4*4 = 16

所以 16 不仅可以被 1 和它本身整除。 (虽然“1”在技术上不是质数,但这是技术性问题,我离题了)

所以我们将 16 除以 1...当然可以,这对每个数字都有效

Divide 16 by 2... we get a remainder of 0  (8*2)
Divide 16 by 3... we get a remainder of 1  
Divide 16 by 4... we get a remainder of 0  (4*4)
Divide 16 by 5... we get a remainder of 1
Divide 16 by 6... we get a remainder of 4
Divide 16 by 7... we get a remainder of 2
Divide 16 by 8... we get a remainder of 0  (8*2)

我们真的只需要一个余数 0 就可以告诉我们它是合数(“素数”的反义词是“合数”)。

检查 16 是否能被 2 整除与检查它是否能被 8 整除是一样的,因为 2 和 8 相乘得到 16。

我们只需要检查频谱的一部分(从 2 到 X 的平方根),因为我们可以乘以的最大数是 sqrt(X),否则我们使用较小的数来获得多余的答案.

17 是素数吗?

17 % 2 = 1
17 % 3 = 2
17 % 4 = 1 <--| approximately the square root of 17 [4.123...]
17 % 5 = 2 <--|
17 % 6 = 5
17 % 7 = 3

sqrt(X) 之后的结果,如 17 % 7 等,是多余的,因为它们必须乘以小于 sqrt(X) 的值才能得出 X。

也就是说,

A * B = X

如果 A 和 B 都大于 sqrt(X) 则

A*B 将产生一个大于 X 的数字。

因此,A 或 B 中的一个必须小于 sqrt(X),检查这两个值是多余的,因为您只需要知道其中一个是否将 X 均分(偶数除法为您提供其他值作为答案)

希望对你有帮助。

编辑:有更复杂的检查素数的方法,Java 在BigInteger class 中有一个内置的“这个数字可能是素数”或“这个数字肯定是复合的”方法,正如我最近通过另一个 SO 答案了解到的那样: ]

【讨论】:

    【解决方案3】:

    您需要对分解数的算法进行一些研究; this wikipedia page 看起来是个不错的起点。在第一段中,它指出:

    当数字非常大时,没有公开有效的整数分解算法......

    但它确实列出了一些特殊和通用的算法。您需要选择一个能够很好地处理 12 个十进制数字的数字。这些数字对于最幼稚的方法来说太大了,但足够小以至于(例如)基于枚举从 2 开始的素数的方法可以工作。 (提示 - 从 Erasthones 筛开始)

    【讨论】:

    • 其实对于这么大的数字,最幼稚的方法应该还是可以的。我使用 600852871609 进行了快速更改,即 775147^2,并且一个天真的测试(尽管使用 C++ 而不是 Java)仍然给出了似乎是瞬时结果的结果(并且时间表示少于 0.1 秒)。
    • @Jerry - 这取决于您测试每个数字是否为素数的天真程度。我试图在这里含糊其辞,以鼓励 OP 自己解决问题。
    【解决方案4】:

    这是一个非常优雅的答案 - 它使用蛮力(不是一些花哨的算法)但以一种聪明的方式 - 通过降低限制,因为我们找到素数并通过这些素数划分复合......

    它也只打印素数 - 并且只打印素数,如果一个素数在产品中多于一次 - 它会打印与该素数在产品中一样多的次数。

        public class Factorization {
        public static void main(String[] args) {
        long composite = 600851475143L;
        int limit = (int)Math.sqrt(composite)+1;
        for (int i=3; i<limit; i+=2)
        {
            if (composite%i==0)
            {
                System.out.println(i);
                composite = composite/i;
                limit = (int)Math.sqrt(composite)+1;
                i-=2;   //this is so it could check same prime again
            }
        }
        System.out.println(composite);
        }
    }
    

    【讨论】:

    • 我刚刚注意到它会完全错过两个因数(尽管在输入数字时很容易看出数字是否是偶数) - 之前可能还有另一个 while(composite%2==0)这个循环来检查它本身有多少次因子 2...
    【解决方案5】:

    您想从 2 -> n-1 迭代并确保 n % i != 0。这是检查素数的最简单方法。如上所述,如果数量很大,这将非常缓慢。

    【讨论】:

      【解决方案6】:

      要查找因子,您需要以下内容:

      long limit = sqrt(number);
      for (long i=3; i<limit; i+=2)
          if (number % i == 0)
              print "factor = " , i;
      

      在这种情况下,这些因素都足够小(

      如果你想稍微优化一下,你可以使用 Eratosthenes 的筛子找到直到平方根的素数,然后只尝试除以素数。在这种情况下,平方根约为 775'000,每个数字只需要一位来表示它是否是素数。您还(通常)只想存储奇数(因为您立即知道除两个之外的所有偶数都是复合数),因此您需要 ~775'000/2 位 = ~47 千字节。

      在这种情况下,这并没有什么真正的回报——即使是一个完全幼稚的算法也会立即产生结果。

      【讨论】:

      • 如果您只是检查 i 是否均分为数,您怎么知道 i 是素数
      • @Krysten:你不会的。正如我所说,这会找到因素,而不仅仅是主要因素。但是,我已经编辑了答案,以添加一种仅显示主要因素的方法。
      • @Krysten:29 是一个素数,因为没有小于 29 的数字可以均匀地分成它。 2 到 28 之间的任何数字都不能除以 29 并且没有余数或小数部分。因此,您可以将一个数字除以其下面的每个整数来确定它是否为素数。您还可以注意到,您只需将其除以每个数字,直到您检查的数字的平方根,这是因为...[我将在答案中详细说明更多空间/格式]
      【解决方案7】:

      我认为你很困惑,因为没有 iff [if-and-only-if] 运算符。

      求整数的平方根是一个很好的捷径。剩下的就是检查该循环中的数字是否均分。这只是 [big number] % i == 0。你的 Prime 函数没有理由。

      由于您正在寻找最大的除数,另一个技巧是从小于平方根的最大整数开始,然后去 i--。

      正如其他人所说,最终,这非常缓慢。

      【讨论】:

      • 您可以随时检查 2,从 3 开始,每一步将 i 增加 2,以将时间减少一半。一种改进这种确定素数的方法的简单方法。
      【解决方案8】:
          private static boolean isPrime(int k) throws IllegalArgumentException
           {
              int j;
      
              if (k < 2) throw new IllegalArgumentException("All prime numbers are greater than 1.");
              else {
                  for (j = 2; j < k; j++) {
                      if (k % j == 0) return false;
                  }
              }
      
              return true;
          }
      
          public static void primeFactorsOf(int n) {
              boolean found = false;
      
              if (isPrime(n) == true) System.out.print(n + " ");
              else {
                  int i = 2;
                  while (found == false) {
                      if ((n % i == 0) && (isPrime(i))) {
                          System.out.print(i + ", ");
                          found = true;
                      } else i++;
                  }
                  primeFactorsOf(n / i);
              }
          }
      

      【讨论】:

      • 无法处理 600851475149。
      【解决方案9】:

      对于那些使用isPrime(int) : boolean 方法的答案,有一种比以前实现的算法更快的算法(类似于)

      private static boolean isPrime(long n) { //when n >= 2
          for (int k = 2; k < n; k++)
              if (n % k == 0) return false;
      
          return true;
      }
      

      就是这样:

      private static boolean isPrime(long n) { //when n >= 2
          if (n == 2 || n == 3) return true;
      
          if (n % 2  == 0 || n % 3 == 0) return false;
      
          for (int k = 1; k <= (Math.floor(Math.sqrt(n)) + 1) / 6; k++)
              if (n % (6 * k + 1) == 0 || n % (6 * k - 1) == 0) return false;
      
          return true;
      }
      

      我使用两个事实制作了这个算法:

      1. 我们只需要检查n % k == 0k &lt;= Math.sqrt(n)。这是真的,因为对于任何更高的因素,因素只是“翻转”前。考虑n = 15 的情况,其中3 * 5 = 5 * 3,并且5 > Math.sqrt(15)。当我们可以只检查其中一个表达式时,没有必要同时检查15 % 3 == 015 % 5 == 0
      2. 所有素数(不包括23)都可以用(6 * k) + 1(6 * k) - 1的形式表示,因为任何正整数都可以用(6 * k) + n的形式表示,其中n = -1, 0, 1, 2, 3, or 4和@987654340 @是整数&lt;= 0n = 0, 2, 3, and 4的情况都是可约的。

      因此,如果n 不能被236k ± 1 &lt;= Math.sqrt(n) 形式的某个整数整除,则它是素数。于是有了上面的算法。

      --

      Wikipedia article on testing for primality

      --

      编辑:认为我不妨发布我的完整解决方案(*我没有使用isPrime(),我的解决方案几乎与最佳答案相同,但我认为我应该回答实际问题):

      public class Euler3 {
      
          public static void main(String[] args) {
              long[] nums = {13195, 600851475143L};
      
              for (num : nums)
                  System.out.println("Largest prime factor of " + num + ": " + lpf(num));
      
          }
      
          private static lpf(long n) {
              long largestPrimeFactor = 1;
              long maxPossibleFactor = n / 2;
      
              for (long i = 2; i <= maxPossibleFactor; i++)
                  if (n % i == 0) {
                      n /= i;
                      largestPrimeFactor = i;
      
                      i--;
                  }
      
                  return largestPrimeFactor;
      
          }
      
      }
      

      【讨论】:

      • 嗨,欢迎来到 Stack Overflow。 :) 600851475149 怎么样,您能否找到代码的两个关键改进,以便在合理的时间内分解该数字? (不幸的是,与其他大多数人一样,这里投票最多的答案并不好)。
      • 嗨!抱歉,我从未看到您的评论,但我认为迟到总比没有好。我想到的一件事是创建一个从 0,0,2,3,0,5,0,7,0... 开始的列表,并且除了 6k-1 和 6k 形式的元素之外的所有元素都包含零+1,数字本身坐在那里,并上升到地板(sqrt(n))。然后,跳过零,划分给定数字的所有实例,存储该数字,然后用 0 替换它及其所有倍数。这结合了 6k-1 和 6k+1 技巧,即埃拉托色尼筛法,以及减少要测试的最大数量。我还缺少什么其他技巧?
      • 嗨。你描述了一个完整的重写。我的意思是,在long maxPossibleFactor = n / 2; for (long i = 2; i &lt;= maxPossibleFactor; i++){ ... } 中进行两次更改。
      • 嗯,也许设置 maxPossibleFactor = floor(sqrt(n)) 而不是 i++,先除以因子 2 和 3,然后每次将 i 增加 2 即 i+=2(然后将 for 循环中的 i-- 替换为 i-=2)。顺便说一句,如何在 cmets 中插入类似代码格式的文本?
      • 增加 2 直到什么时候? 是个问题。对于代码格式,请将代码括在反引号 (`...`) 中。如果代码包含反引号,请用双反引号括起来。
      【解决方案10】:

      求所有素因数分解

      import java.math.BigInteger;
      import java.util.Scanner;
      
      
      public class BigIntegerTest {
      
      
           public static void main(String[] args) {
      
      
                  BigInteger myBigInteger = new BigInteger("65328734260653234260");//653234254
                  BigInteger originalBigInteger;
                  BigInteger oneAddedOriginalBigInteger;
                  originalBigInteger=myBigInteger;
                  oneAddedOriginalBigInteger=originalBigInteger.add(BigInteger.ONE);
                  BigInteger index;
                  BigInteger countBig;
      
      
                  for (index=new BigInteger("2");  index.compareTo(myBigInteger.add(BigInteger.ONE)) <0; index = index.add(BigInteger.ONE)){
      
                      countBig=BigInteger.ZERO;
                      while(myBigInteger.remainder(index) == BigInteger.ZERO ){
                          myBigInteger=myBigInteger.divide(index);
                          countBig=countBig.add(BigInteger.ONE);
                      }
      
                      if(countBig.equals(BigInteger.ZERO)) continue;
                      System.out.println(index+ "**" + countBig);
      
                  }
                  System.out.println("Program is ended!");
           }
      }
      

      【讨论】:

        【解决方案11】:

        我的编程课遇到了一个非常相似的问题。在我的课上,它必须计算输入的数字。我使用了与 Stijak 非常相似的解决方案。我编辑了我的代码来计算这个问题的数字,而不是使用输入。

        与 Stijak 的代码的一些区别如下:

        我在代码中考虑了偶数。

        我的代码只打印最大的素因子,而不是所有因子。

        在我将当前因子的所有实例划分为 off 之前,我不会重新计算 factorLimit。

        我声明了所有变量,因为我希望灵活地使用它来处理非常大的数字值。我发现最坏的情况是像 9223372036854775783 这样的非常大的素数,或者像 9223371994482243049 这样的素数平方根非常大的数。一个数的因子越多,算法运行的速度就越快。因此,最好的情况是 4611686018427387904 (2^62) 或 6917529027641081856 (3*2^61) 这样的数字,因为两者都有 62 个因数。

        public class LargestPrimeFactor
        {
            public static void main (String[] args){
                long number=600851475143L, factoredNumber=number, factor, factorLimit, maxPrimeFactor;
                while(factoredNumber%2==0)
                    factoredNumber/=2;
                factorLimit=(long)Math.sqrt(factoredNumber);
                for(factor=3;factor<=factorLimit;factor+=2){
                    if(factoredNumber%factor==0){
                        do  factoredNumber/=factor;
                        while(factoredNumber%factor==0);
                        factorLimit=(long)Math.sqrt(factoredNumber);
                    }
                }
                if(factoredNumber==1)
                    if(factor==3)
                        maxPrimeFactor=2;
                    else
                        maxPrimeFactor=factor-2;
                else
                    maxPrimeFactor=factoredNumber;
                if(maxPrimeFactor==number)
                    System.out.println("Number is prime.");
                else
                    System.out.println("The largest prime factor is "+maxPrimeFactor);
            }
        }
        

        【讨论】:

          【解决方案12】:
          public class Prime
          {
           int i;   
          
           public Prime( )
           {
              i = 2;
           }
          
           public boolean isPrime( int test ) 
           {
              int k;
          
              if( test < 2 )
                  return false;
              else if( test == 2 )  
                  return true;
              else if( ( test > 2 ) && ( test % 2 == 0 ) )
                  return false;
              else
              {
                  for( k = 3; k < ( test/2 ); k += 2 )
                  {
                      if( test % k == 0 ) 
                          return false;
                  }
          
              }
          
              return true;
          
           }
          
           public void primeFactors( int factorize )
           {
              if( isPrime( factorize ) )
              {
                  System.out.println( factorize );
                  i = 2;
              }
              else
              {
                  if( isPrime( i ) && ( factorize % i == 0 ) )
                  {
                      System.out.print( i+", " );
                      primeFactors( factorize / i );
                  }
                  else
                  {
                      i++;
                      primeFactors( factorize );
                  }
             }
          
             public static void main( String[ ] args )
             {
                 Prime p = new Prime( );
          
                 p.primeFactors( 649 );
                 p.primeFactors( 144 );
                 p.primeFactors( 1001 );
             }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-03-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-01-28
            • 1970-01-01
            相关资源
            最近更新 更多