如何加快我的主要计算?

时间:2014-02-24 18:05:06

标签: java performance iterator primes java-8

我正在尝试回答以下欧拉问题(#10):

  

低于10的素数之和为2 + 3 + 5 + 7 = 17.

     

找出200万以下所有素数的总和。

我的程序运行正常,但是我发现计算它需要100秒,使用以下代码,以new Problem10().run()为起点:

public class Problem10 extends Problem<Long> {
    @Override
    public void run() {
        result = Iterators.finiteLongStream(new PrimeGenerator(), i -> i <= 2_000_000)
                .sum();
    }

    @Override
    public String getName() {
        return "Problem 10";
    }
}

public abstract class Iterators {
    ///

    public static PrimitiveIterator.OfLong finiteLongIterator(final PrimitiveIterator.OfLong iterator, final LongPredicate predicate) {
        return new PrimitiveIterator.OfLong() {
            private long next;

            @Override
            public boolean hasNext() {
                if (!iterator.hasNext()) {
                    return false;
                }
                next = iterator.nextLong();
                return predicate.test(next);
            }

            @Override
            public long nextLong() {
                return next;
            }
        };
    }

    public static LongStream finiteLongStream(final PrimitiveIterator.OfLong iterator, final LongPredicate predicate) {
        return Iterators.longStream(Iterators.finiteLongIterator(iterator, predicate));
    }

    public static LongStream longStream(final PrimitiveIterator.OfLong iterator) {
        return StreamSupport.longStream(
                Spliterators.spliteratorUnknownSize(iterator, 0), false
        );
    }

    ///
}

public class PrimeGenerator implements PrimitiveIterator.OfLong {
    private final static LongNode HEAD_NODE = new LongNode(2);

    private LongNode lastNode = HEAD_NODE;
    private long current = 2;

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public long nextLong() {
        if (lastNode.value == current) {
            if (lastNode.next != null) {
                long old = lastNode.value;
                lastNode = lastNode.next;
                current = lastNode.value;
                return old;
            }
            return current++;
        }
        while (true) {
            if (isPrime(current)) {
                appendNode(current);
                return current++;
            }
            current++;
        }
    }

    private boolean isPrime(final long number) {
        LongNode prime = HEAD_NODE;
        while (prime != null && prime.value <= number) {
            if (number % prime.value == 0) {
                return false;
            }
            prime = prime.next;
        }
        return true;
    }

    private void appendNode(final long value) {
        LongNode newNode = new LongNode(value);
        couple(lastNode, newNode);
        lastNode = newNode;
    }

    private void couple(final LongNode first, final LongNode second) {
        first.next = second;
        second.previous = first;
    }

    private static class LongNode {
        public final long value;

        public LongNode previous;
        public LongNode next;

        public LongNode(final long value) {
            this.value = value;
        }
    }
}

我该如何优化?如果可能的话,首先根据我当前的代码提出建议,然后提出完全不同的算法。

编辑,我也想避免使用有限的Sieve of Eratosthenes,作为这样一个迭代器的重点。流是为了能够以无限的价格来做到这一点,我不确定自己的Eratosthenes方法是否适用于无数的数字,我认为这不是一件容易的事。

2 个答案:

答案 0 :(得分:1)

如果你观察到只需要考虑小于数字平方根的素数因子,那么方法isPrime()中的迭代次数可以减少。

所以目前的情况是:

  while (prime != null && prime.value <= number) 

可以改为:

   while (prime != null && prime.value <= square_root(number) )

可能还有其他可能性来优化您的代码,但这需要详细检查您的代码。

答案 1 :(得分:0)

以下是一些想法(不是代码,因为这似乎是家庭作业/项目问题):

  • 使用ints计算不长(int足够容纳2 000 000)虽然你的素数总和可能需要很长
  • 只检查循环中以3开头的奇数(为什么要检查一个你知道不是素数的偶数&gt;!
  • 确保尽可能使用最少的代码..代码中有很多结构。不会对int值进行简单的循环(并且检查只发现值的sqrt的素数吗?)

不要使用平方根功能。而是计算最大素数的指数,你必须检查为素数[p] *素数[p],只要它大于你的试验值。例如,在你的代码中使用这样的东西(其中ps是你正在检查的第一个素数的索引,而iMax在进入循环之前是素数[ps] * primes [ps]。为了提高效率,总是使用“当你可以时,可以减少计算量。

while (i > iMax) { 
    ps++; iMax = primes[ps]*primes[ps];
};