如何使用额外的线程以更快的方式确定数字是否为素数?

时间:2014-11-21 15:00:24

标签: java multithreading primes

我有一个PrimeNumbers类程序。它显示x是否为素数。 x是程序中分析的数字。

程序需要多长时间才能知道答案。 x很大,知道答案需要9秒钟。如何使用更多线程更快地运行程序?在这种情况下,我很难掌握如何实现线程。

public class PrimeNumbers {
    private static int x = 2147483647;

    public static boolean prime= true;

    public static void main(String[]args){

        long start,  end, elapsetime;
        start= System.currentTimeMillis();

        for(int y=2; y<x; y++){
            if(x % y == 0){
                prime=false;
                System.out.println(y);
                break;
            }
        }

        end = System.currentTimeMillis();

        elapsetime = end - start;
        System.out.println("Prime: " + prime);

        System.out.println(elapsetime+ " mill sec " + (elapsetime / 1000 
            + " seconds."));

    }       
}

4 个答案:

答案 0 :(得分:3)

我会忽略您是否拥有最有效的方法,并专注于如何通过更多线程更快地使用当前代码。

您目前正在遍历2 -> x中的所有数字并执行简单测试。提高性能的方法可能是将此任务拆分为 Z 块并启动 Z 线程以并行执行测试。

E.g。如果你有两个线程,你将有一个线程检查2 -> x/2,另一个检查x/2 + 1 -> x。如果全局(可能是volatile)标志设置为true,则每个线程都应该从测试中断,这表明另一个线程已经反驳了该素数。

答案 1 :(得分:2)

您的素性测试效率非常低,您在每个小于x的数字上循环。
你怎么能改善它?这link should be helpful.

一个好的算法是AKS测试,或者是Eratosthenes的Sieve。下面的代码实现了wiki文章中的一种算法,它比你发布的测试更有效。

public static boolean isPrime(long n) { 

    // http://en.wikipedia.org/wiki/Primality test

    if (n <= 3) return n > 1;
    if (n % 2 == 0 || n % 3 == 0) return false;
    for (int i = 5; i*i <=n; i+=6) {
      if (n % i == 0 || n % (i+2) == 0) return false; 
    }
    return true;
  }
}

答案 2 :(得分:1)

如果你想要一个更好的算法,Munyari已经建议了一个。

忽略以下示例可以帮助您如何并行执行算法(即使它是一个愚蠢的算法)

我们需要一个实现Callable接口的类(类似于Runnable)。它应该得到工作的一部分并计算它。

public class PrimeChecker implements Callable<Boolean> {

    private final long numberToCheck;
    private final long start;
    private final long end;

    public PrimeChecker(long numberToCheck, long start, long end) {
        this.numberToCheck = numberToCheck;
        this.start = start;
        if (end >= numberToCheck) {
            this.end = numberToCheck - 1;
        }else{
            this.end = end;
        }
        System.out.println("A PrimeChecker with start " + start + " and end " + end + " values to check number "
                + numberToCheck);
    }

    @Override
    public Boolean call() throws Exception {
        boolean prime = true;
        long current = start;

        if (current != 2 && (current % 2 == 0)) {
            current = current + 1;
        }

        for (; current < end; current = current + 2) {
            if (numberToCheck % current == 0) {
                prime = false;
                System.out.println("The number " + numberToCheck + " is divisable with " + current);
                return prime;
            }
        }
        return prime;
    }

} 

它只是start来自一个数字并检查给定数字numberToCheck是否可以分割并继续直到它达到数字end

在Main类中,我们必须创建多个PrimeChecker作业并并行执行它们。为此,我们使用Java的ExecutorService。它为我们创建了一个线程池。然后我们可以在多个PrimeCheckers上分配工作。最后,我们执行invokeAll ExecutorService方法。这为我们提供了一个Future列表,其中包含我们并行执行的每个作业的结果。

public class Main {

    public static boolean prime= true;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        long startTime = System.currentTimeMillis();


        long numberToCheck = 5333334345L;
        int numberOfThreads = 10;
        System.out.println("Checking if the number " + numberToCheck + " ...");

        ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);

        List<PrimeChecker> primeCheckers = new ArrayList<PrimeChecker>();

        long partOfNumber = (long) Math.ceil((double)numberToCheck/ numberOfThreads);
        long start = 2 ;
        long end = 0;
        for(int i = 0; i < numberOfThreads; i++){
            end = end + partOfNumber;
            primeCheckers.add(new PrimeChecker(numberToCheck, start, end));
            start = end+1;
        }

        List<Future<Boolean>> futures = executor.invokeAll(primeCheckers);

        for(Future<Boolean> future : futures){
            prime = future.get();
            if(prime == false){
                break;
            }
        }

        System.out.println("The number " + numberToCheck + " is " + (prime ? "a prime" :"NOT !!!!!!!!!!!!!!!!!!!! a prime") + " number");

        long endTime = System.currentTimeMillis();
        long elapsetime = endTime - startTime;
        System.out.println(elapsetime + " milliseconds");

        System.exit(0);
    }
}

您可以尝试使用不同数量的线程(请参阅numberOfThreads变量)来查看差异。

我希望它能够帮助您更好地理解多线程。 (注意:它只是整个线程主题的一小部分)

答案 3 :(得分:0)

如果您不需要自己实施质量检查,我建议使用API​​。您可以根据需要控制确定性。在该示例中,它是:1-(1/2) 100

public static void main(String[] args) {
    BigInteger mersenne = new BigInteger("2").pow(521).add(BigInteger.ONE.negate());
    System.out.println("digits of the number: " + mersenne.toString().length());

    long start = System.currentTimeMillis();
    final int certainty = 100;
    boolean isPrime = mersenne.isProbablePrime(certainty);
    System.out.println("elapsed millis: " + (System.currentTimeMillis() - start));
    System.out.println("isPrime       : " + isPrime);
}

修改 这是建议示例的优化版本。

public class PrimeNumbers {
    private static int x = 2147483647;
    public static boolean prime= true;

    public static void main(String[]args){

        long start, end, elapsetime;
        int divisor = 1;

        start= System.currentTimeMillis();

        if (x % 2 == 0) {
            prime = false;
            divisor = 2;
        } else {
            // - you can use an increment of two
            // because you don't need to check
            // for a divisor which is a multiple
            // of two
            // - you don't need to check for any divisor
            // which is greater than x/2
            for(int y=3; y < x/2; y += 2){
                if(x % y == 0){
                    prime=false;
                    divisor = y;
                    break;
                }
            }
        }

        end = System.currentTimeMillis();

        System.out.println("Prime: " + prime);
        if (!prime) {
            System.out.println("divisible by: " + divisor);
        }

        elapsetime = end - start;
        System.out.println(elapsetime+ " mill sec " + (elapsetime / 1000
            + " seconds."));
    }
}