我正在解决Codility问题 CountSemiprimes:计算给定区间[a..b]中的半质数个数.
任务描述
素数是一个正整数X,它恰有两个不同的因子:1和X。前几个质数是2、3、5、7、11和13。
半质数是两个(不一定不同)质数的积。前几个半质数是4、6、9、10、14、15、21、22、25、26。
给定两个非空数组P和Q,每个数组都包含M个整数。这些数组表示关于指定范围内半质数数量的查询。
查询K要求您找到范围(P[K],Q[K])内的半质数数量,其中1≤P[K]≤Q[K]≤N。
编写一个高效的算法,满足以下条件:
- N是在[1..50,000]范围内的整数;
- M是在[1..30,000]范围内的整数;
- 数组P,Q的每个元素都是在[1..N]范围内的整数;P[i] ≤ Q[i]。
我的解决方案
我的当前得分为66%,性能问题是针对大数据集:
- 大随机数,长度=〜30,000
- 所有最大范围
测试表明,它应该花费约2秒钟,但我的解决方案需要超过7秒钟。
这是我目前的解决方案
class Solution {
private static List<Integer> getPrimes(int max) {
List<Integer> primes = new ArrayList<>(max / 2);
for (int i = 0; i < max; i++)
if (isPrime(i))
primes.add(i);
return primes;
}
private static boolean isPrime(int val) {
if (val <= 1)
return false;
if (val <= 3)
return true;
for (int i = 2, sqrt = (int)Math.sqrt(val); i <= sqrt; i++)
if (val % i == 0)
return false;
return true;
}
private static boolean[] getSemiPrimes(int N) {
List<Integer> primes = getPrimes(N);
boolean[] semiPrimes = new boolean[N + 1];
for (int i = 0; i < primes.size(); i++) {
if (primes.get(i) > N)
break;
for (int j = i; j < primes.size(); j++) {
if (primes.get(j) > N || N / primes.get(i) < primes.get(j))
break;
int semiPrime = primes.get(i) * primes.get(j);
if (semiPrime <= N)
semiPrimes[semiPrime] = true;
}
}
return semiPrimes;
}
public static int[] solution(int N, int[] P, int[] Q) {
boolean[] semiPrimes = getSemiPrimes(N);
int[] res = new int[P.length];
for (int i = 0; i < res.length; i++)
for (int j = P[i]; j <= Q[i]; j++)
if (semiPrimes[j])
res[i]++;
return res;
}
}
有关提高性能的任何想法?我上次的建议是使用数组代替Set
来存储半素数。这对于解决一些性能测试非常有帮助。
for
循环到sqrt(n)
是找到所有质数[0...n]
最有效的方法。 - oleg.cheredniki += 2
而不是i++
,或者只需检查形式为6*i ± 1
的值的可除性。筛法始终是生成质数列表的最佳方法。您已经错误地进行了基准测试。 - phuclvsqrt(n)
的for循环可能是确定一个数是否为质数的最快方法。但是,生成素数列表的最快方法并不是它。对于这个目的,筛法要快得多。 - marstran