简体   繁体   English

如何并发Prime分解?

[英]How to Concurrent Prime Factorization?

The following code snippet calculate prime factors of a given number: 以下代码段计算给定数字的素数因子:

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

It takes 140937ms to calculate prime factors of 9223372036854775783 (it is a last prime less than Long.MAX_VALUE ). 计算9223372036854775783的素数因子需要140937ms(它是一个小于Long.MAX_VALUE的最后一个素数)。 Is there any way to implement this factorization by concurrency ie, by using ExecutorService ? 有没有办法通过并发实现这种分解,即使用ExecutorService

Edit: 编辑:

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

}

2nd Edit: 第二编辑:

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

Now code snippet: 现在代码片段:

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

is giving the output: 给出输出:

Result: 600851475143
Result: 6857

Note: The class name is Factorization and I changed the name of the method getPrimeFactors to getPrimeFactorsByConcurrentGeneralMethod 注意:类名是Factorization ,我将方法getPrimeFactors的名称更改为getPrimeFactorsByConcurrentGeneralMethod

Uh before you start thinking about concurrent implementations, I'd propose optimizing the algorithm a bit. 呃,在你开始考虑并发实现之前,我建议稍微优化算法。 Apart from 2 every prime is odd, so make 2 a special case and then start at 3 with your loop and increase the factor by 2. Then instead of computing number / factor every loop ending (that also makes optimizing for the JIT harder I think) just compute Sqrt(N) once - after all we know that every number can have only one prime factor > sqrt(N) ;) 除了2之外,每个素数都是奇数,所以将2作为一个特殊情况,然后从你的循环开始3并将因子增加2.然后代替计算每个循环结束的数量/因子(这也使JIT的优化更难我认为)只计算一次Sqrt(N) - 毕竟我们知道每个数字只能有一个素因子> sqrt(N);)

If you've done that I'd change your method signature so that you don't always start at 3 and work up to Sqrt(N) but give it start and end ranges. 如果你已经这样做了,我会改变你的方法签名,这样你就不会总是从3开始并且最多可以达到Sqrt(N)但是给它开始和结束范围。 The simplest solution would be to split the range from 3-Sqrt(N) into K parts where K is the number of cores available (since this is not really balanced using smaller parts may give you a better load balancing) and throw that into an executioner service. 最简单的解决方案是将范围从3-Sqrt(N)分成K个部分,其中K是可用内核的数量(因为使用较小的部分不能实现平衡可能会给您带来更好的负载平衡)并将其转换为刽子手服务。 You then just have to gather all results and create one list from all the smaller lists. 然后,您只需收集所有结果并从所有较小的列表中创建一个列表。

Just note that this simple approach does more work for BigIntegers since you always compute the values on the start number and every division algorithm's complexity somewhere depends on the bitsize - you can solve that as well if you eg use smaller job sizes and synchronize between those. 请注意,这个简单的方法为BigIntegers做了更多的工作,因为你总是计算起始编号的值,并且每个除法算法的复杂性在某处取决于bitsize - 如果你使用较小的作业大小并在它们之间进行同步,你也可以解决这个问题。

PS: Note that your split range algorithm still has to handle the case 2 and sqrt(n) correctly. PS:请注意,您的分割范围算法仍然必须正确处理案例2和sqrt(n)。

PPS: And I hope you're aware that this problem is in NP and you're only doing this to learn a bit about concurrency. PPS:我希望你知道这个问题出现在NP中,你只是这样做才能学习并发性。

No, there are no such easy method, at least known one. 不,没有这么简单的方法,至少是已知的方法。 The problem of optimal integer factorization is still open in math. 数学中仍然存在最优整数分解的问题。

You could use an Elliptic Curve Method (ECM) Prime Factorization . 您可以使用椭圆曲线方法(ECM)Prime分解 It is suited well for a parallel computation. 它非常适合并行计算。 But the method itself is not trivial - several thousands lines of code. 但是这个方法本身并不简单 - 几千行代码。 Sources are available for example here 例如, 此处提供了来源

You could tweak your implementation in some ways: 您可以通过以下方式调整实现:

  1. Avoid unnecessary autoboxing as Christian Semrau already mentioned in his comment. 避免不必要的自动装箱,正如Christian Semrau在评论中已提到的那样。
  2. Create a shortcut for the "simple" case, eg you iterate over every number between 2 and number/2. 为“简单”案例创建快捷方式,例如,迭代2和number / 2之间的每个数字。 This is unnecessary since 2 is the only even prime factor. 这是不必要的,因为2是唯一的平均因素。 You will save half of the number of iterations with this shortcut in the best case. 在最佳情况下,您将使用此快捷方式保存一半的迭代次数。
  3. You don't need to calculate the prime factors of number , sqrt(number) is sufficient. 你并不需要计算的主要因素numbersqrt(number)就足够了。
  4. There are more efficient ways to 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; } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM