Search code examples
javaalgorithmperformancebigintegerperfect-numbers

How can I improve my Java code to generate all known Perfect numbers?


I'm trying to generate all known perfect numbers using Euclid–Euler theorem,

I was wondering if I can modify/rewrite my code to get the results quickly.

Here is my code:

   public static BigInteger[] genAllPerfect(int howMany)
   {
      int[] expn = { 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689,
            9941, 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269,
            2976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, 32582657, 37156667, 42643801,
            43112609, 57885161, 74207281, 77232917, 82589933 };

      BigInteger[] perfectNums = new BigInteger[51];
      BigInteger One = BigInteger.ONE;
      BigInteger Two = One.add(One);

      for (int i = 0; i < howMany; i++)
      {
         BigInteger firstPart = Two.pow(expn[i] - 1); // 2^(p-1)

         BigInteger secondPart = Two.pow(expn[i]); // 2^p

         secondPart = secondPart.subtract(One); // (2^p - 1)

         perfectNums[i] = firstPart.multiply(secondPart);
      }

      return perfectNums;
   }

This code takes 30 sec in average. Thanks.


Solution

  • I rewrite the formula to fit with Java BigInteger class setBit() method to reduce time.

    2^(p-1) * (2^p -1)
    
    = (2^p)/2 * (2^p -1)
    
    = ((2^p) * (2^p -1))/2
    
    = (2^2p - 2^p)/2
    
    = (2^(2p-1) - 2^(p-1))
    

    Since 2^n can be calculate fast using BigInteger setBit() method. setBit is the fastest since it works with a single bit only.

    Here is the complete code, it takes around 0.35 seconds for all 51 perfect numbers in my machine.

    public class PerfectNumbers
    {
       static BigInteger[] genAllPerfect(int howMany)
       {
          if (howMany > 51)
             howMany = 51;
    
          int[] expn = { 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689,
                9941, 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269,
                2976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, 32582657, 37156667, 42643801,
                43112609, 57885161, 74207281, 77232917, 82589933 };
    
          BigInteger Zero = BigInteger.ZERO;
          BigInteger[] perfectNums = new BigInteger[howMany];
    
          for (int i = 0; i < howMany; i++)
          {
             BigInteger perfect1 = Zero.setBit(expn[i] - 1); // 2^(p-1)
    
             perfectNums[i] = Zero.setBit(2 * expn[i] - 1); // 2^(2*p-1)
    
             perfectNums[i] = perfectNums[i].subtract(perfect1); // 2^(2*p-1) - 2^(p-1)
          }
          return perfectNums;
       }