【问题标题】:How to Concurrent Prime Factorization?如何并发素数分解?
【发布时间】:2011-06-26 13:17:34
【问题描述】:

下面的代码sn-p计算给定数的素因数:

public static LinkedList<Long> getPrimeFactors(Long number) {
    LinkedList<Long> primeFactors = new LinkedList<Long>();

    for (Long factor = Long.valueOf(2); factor <= number / factor; factor++) {
        if (number % factor == 0) {
            primeFactors.add(factor);
            while (number % factor == 0) {                  
                number /= factor;
            }
        }           
    }

    if (number > 1) {
        primeFactors.add(number);
    }

    return primeFactors;
}

计算 9223372036854775783 的素数需要 140937 毫秒(它是小于Long.MAX_VALUE 的最后一个素数)。有没有办法通过并发实现这种分解,即使用ExecutorService

编辑:

public static void getPrimeFactors(Long number) {
    LinkedList<Long> primeFactors = new LinkedList<Long>();

    if (number % 2 == 0) {
        primeFactors.add(2L);

        while (number % 2 == 0) {
            number /= 2;
        }
    }

    long limit = (long) Math.sqrt(number) + 1;

    ExecutorService service = Executors.newFixedThreadPool(2);
    LinkedList<Future<LinkedList<Long>>> futures = new LinkedList<Future<LinkedList<Long>>>();
    futures.add(service.submit(new PrimeFactor(3, limit / 2, number)));
    futures.add(service.submit(new PrimeFactor(1 + limit / 2, limit, number)));

    for (Future<LinkedList<Long>> future : futures) {
        try {
            primeFactors.addAll(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    service.shutdown();

    if(number>1) {
        primeFactors.add(number);           
    }

    System.out.println(primeFactors);
}

private static class PrimeFactor implements Callable<LinkedList<Long>> {
    private long lowerLimit;
    private long upperLimit;
    private Long number;

    public PrimeFactor(long lowerLimit, long upperLimit, Long number) {
        this.lowerLimit = lowerLimit;
        this.upperLimit = upperLimit;
        this.number = number;
    }

    public LinkedList<Long> call() throws Exception {
        LinkedList<Long> primeFactors = new LinkedList<Long>();
        for (long i = lowerLimit; i < upperLimit; i += 2) {
            if (number % i == 0) {
                primeFactors.add(i);
                while (number % 2 == 0) {
                    number /= i;
                }
            }
        }
        return primeFactors;
    }

}

第二次编辑:

public static LinkedList<Long> getPrimeFactorsByFastGeneralMethod(long number) {
    LinkedList<Long> primeFactors = new LinkedList<Long>();

    if (number % 2 == 0) {
        primeFactors.add(2L);

        while (number % 2 == 0) {
            number /= 2;
        }
    }

    long limit = (long) Math.sqrt(number);

    for (long factor = 3; factor <= limit; factor += 2) {
        if (number % factor == 0) {
            primeFactors.add(factor);
            while (number % factor == 0) {
                number /= factor;
            }
        }
    }

    if (number > 1) {
        primeFactors.add(number);
    }

    return primeFactors;
}

现在代码 sn-p:

    LinkedList<Long> primeFactors = Factorization.getPrimeFactorsByConcurrentGeneralMethod(600851475143L);
    System.out.println("Result: " + primeFactors.get(primeFactors.size() - 1));

    primeFactors = Factorization.getPrimeFactorsByFastGeneralMethod(600851475143L);
    System.out.println("Result: " + primeFactors.get(primeFactors.size() - 1));

正在给出输出:

Result: 600851475143
Result: 6857

注意:类名是Factorization,我把方法名getPrimeFactors改成了getPrimeFactorsByConcurrentGeneralMethod

【问题讨论】:

  • 第一个明显的改进是将Long 变量替换为long 变量。这减少了装箱和拆箱包装器对象的开销。
  • @Christian Semrau 主要是我用BigInteger 代替Long 写的。

标签: java concurrency prime-factoring


【解决方案1】:

嗯,在您开始考虑并发实现之前,我建议您稍微优化一下算法。除了 2 之外,每个素数都是奇数,因此将 2 设为特殊情况,然后从 3 开始使用循环并将因子增加 2。然后不是计算每个循环结束的数字/因子(这也使得 JIT 的优化更难我认为) 只需计算一次 Sqrt(N) - 毕竟我们知道每个数字只能有一个素因子 > sqrt(N) ;)

如果你这样做了,我会更改你的方法签名,这样你就不会总是从 3 开始并一直工作到 Sqrt(N),而是给它开始和结束范围。最简单的解决方案是将范围从 3-Sqrt(N) 拆分为 K 个部分,其中 K 是可用的内核数(因为这不是真正平衡的,使用较小的部分可能会给您带来更好的负载平衡)并将其放入刽子手服务。然后,您只需收集所有结果并从所有较小的列表中创建一个列表。

请注意,这种简单的方法对 BigIntegers 有更多的工作,因为您总是计算起始编号的值,并且每个除法算法的复杂性取决于位大小 - 如果您使用较小的作业大小并同步,您也可以解决这个问题之间。

PS:请注意,您的分割范围算法仍然必须正确处理案例 2 和 sqrt(n)。

PPS:我希望您知道这个问题存在于 NP 中,您这样做只是为了了解一点并发性。

【讨论】:

  • @Voo 我已按照您的指示添加了修改版本。你在说这个吗?没事吧?我将2*101*329569479697L 作为参数传递,它正在打印[2, 101, 33286517449397]。但是我很难将范围拆分为 K,其中 k=number_of_core。你能告诉我我该怎么做吗?谢谢。分解素数 9223372036854775783L 需要 14188 毫秒。
  • 是的,看起来不错。使用包含的下限和互斥的上限也很好(使所有计算更简单)。要获得您的 k,只需使用 Runtime.getRuntime().availableProcessors();然后只使用一个简单的循环。请注意,正如 nulldevice 所说,有比简单的蛮力方法更复杂的算法,但它是一种玩并发性的好方法(现在你可以看看每个分割需要多长时间才能执行 - 我很确定赢了'不平衡,所以尝试一下,例如)#
  • @Voo 非常抱歉。我想我犯了一些错误。我把它们写进了我原来的帖子。
  • 没有太多时间彻底查看是否存在更普遍的问题,但您的调用语句中有一个“while (number % 2 == 0)”,显然应该是 number % i
  • 此外,由于明显的原因,您的 (number > 1) 支票也将不起作用
【解决方案2】:

不,没有这样简单的方法,至少是已知的方法。最优整数分解的问题在数学中仍然悬而未决。

您可以使用Elliptic Curve Method (ECM) Prime Factorization。它非常适合并行计算。但方法本身并不简单——几千行代码。来源可用,例如 here

【讨论】:

    【解决方案3】:

    您可以通过某些方式调整您的实现:

    1. 避免不必要的自动装箱,正如 Christian Semrau 在他的评论中已经提到的那样。
    2. 为“简单”案例创建快捷方式,例如。 G。您遍历 2 和 number/2 之间的每个数字。这是不必要的,因为 2 是唯一的偶数因数。在最好的情况下,使用此快捷方式可以节省一半的迭代次数。
    3. number的素因数不用计算,sqrt(number)就足够了。
    4. Integer factorization有更有效的方法

      public static List<Long> getPrimeFactors(long number) {
          List<Long> primeFactors = new ArrayList<Long>();
      
          // Only process natural numbers
          if(number < 1l) {
              return primeFactors;
          }
      
          // The only prime factor of 1 is 1
          if(number == 1l) {
              primeFactors.add(1l);
              return primeFactors;
          }
      
          // Even have the prime factor 2
          if(number % 2l == 0l) {
              primeFactors.add(2l);
      
              while(number % 2l == 0l) {
                  number /= 2l;
              }
          }
      
          // Iterate from 3 to sqrt(number) to calculate the remaining prime factors
          for (long factor = 3l; factor < Math.sqrt(number); factor+=2l) {
              if (number % factor == 0) {
                  primeFactors.add(factor);
                  while (number % factor == 0) {                  
                      number /= factor;
                  }
              }           
          }
      
          if (number > 1) {
              primeFactors.add(number);
          }
      
          return primeFactors;
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-20
      相关资源
      最近更新 更多