高效素数计算程序 Java
Efficient Prime Number Computing Program Java
所以我试图在 java 中制作一个素数计算器,它从 1 到用户给定范围的上限,并打印出所有素数。
解决这个问题的一种方法是简单地使用嵌套 for 循环并测试范围内每个数字是否被小于它的所有数字整除。
不过,我认为只测试质数会更有效,以避免重复因子并加快程序速度。
例如,如果我们的数字是 16,而不是测试它是否可以被 2,3,4,5,...14,15,16 整除,我只能测试 2,3, 5,7,11,13 并在找到一个因子后停止。
所以我尝试创建一个数组来存储到目前为止找到的所有素数,并且只使用这些值来测试下一个数字。
这是我的代码,我不明白为什么它不起作用
Scanner sc = new Scanner (System.in);
System.out.print ("Enter the upper limit: ");
int high = sc.nextInt();
boolean isPrime = true;
int[] primearray = new int[0];
for (int num = 1; num <= high; num++)
{
for (int x: primearray)
{
if (num%x==0)
{
isPrime = false;
break;
}
}
if (isPrime == true) {
int size = primearray.length;
primearray = new int[size+1];
primearray [size] = num;
System.out.println (num);
}
}
首先,1 不是素数。通过从 1 开始你的外循环,你的素数数组以 1 结束,然后其他所有内容都将被测试为不是素数。在 int num = 2
.
开始你的外循环
其次,当您扩展 primearray
时,您并没有复制现有的已知素数。您可以使用
primearray = Arrays.copyOf(primearray, size+1);
这将创建一个包含所有旧内容的新数组,并 space 多一个值。
最后,您可能想查看 Sieve of Eratosthenes. A careful implementation of that algorithm will be more efficient than your current algorithm, which requires an expensive array reallocation every time you find a prime. You can use a BitSet
以跟踪筛子需要的标志。
正如您所说,关键的简化是仅在找到下一个素数时才测试素数。例如:
public class PrimeGenerator {
private long current = 1;
private final List<Long> primes = new ArrayList<>();
public long next() {
do {
current++;
} while (primes.stream().anyMatch(n -> current % n == 0));
primes.add(current);
return current;
}
public LongStream stream() {
return LongStream.generate(this::next);
}
}
这会记录每个生成的素数。
您可以使用
将所有素数生成为特定值
generator.stream().takeWhile(p -> p < value)...
你逻辑有问题
boolean isPrime = true;
这个变量应该在for
循环中声明,想象一下,如果你发现4
不是素数那么isPrime = false
,那么你检查5
但是没有设置 isPrime = true
.
的任何代码块
而这个区块:
if (isPrime == true) {
int size = primearray.length;
primearray = new int[size+1];
primearray [size] = num;
System.out.println (num);
}
您创建了新的素数数组,primearray
,大小增加了 1,因此 primearray
不包含任何旧素数,这会在检查素数时出错。所以你需要将旧素数复制到新数组中。
并且因为素数以 2 开头,所以您的代码应该是:
Scanner sc = new Scanner(System.in);
System.out.print("Enter the upper limit: ");
int high = sc.nextInt();
int[] primeArray = new int[0];
for (int num = 2; num <= high; num++)
{
boolean isPrime = true;
for (int x : primeArray)
{
if (num % x == 0)
{
isPrime = false;
break;
}
}
if (isPrime == true)
{
primeArray = Arrays.copyOf(primeArray, primeArray.length + 1);
primeArray[primeArray.length - 1] = num;
System.out.println(num);
}
}
之前的回答已经解释了你的代码有什么问题。
我只想分享另一种更有效的方法。示例实现如下所示。基本上,一旦我们知道 x 是素数,我们也知道 i*x 不是素数。可以进一步阅读和可视化 here
public int countPrimes(int n) {
if (n == 0 || n == 1) return 0;
int count = 0;
boolean[] check = new boolean[n+1];
for (int i = 2; i < n; i++) {
if (check[i]) continue;
for (int j = 1; j <= n / i; j++) {
check[j * i] = true;
}
count++;
}
return count;
}
您应该使用埃拉托色尼筛法来查找素数,而不是测试每个数的可整除性;该方法比筛分慢得多。这是从 my blog:
筛选的实现
public static LinkedList sieve(int n)
{
BitSet b = new BitSet(n);
LinkedList ps = new LinkedList();
b.set(0,n);
for (int p=2; p<n; p++)
{
if (b.get(p))
{
ps.add(p);
for (int i=p+p; i<n; i+=p)
{
b.clear(i);
}
}
}
return ps;
}
所以我试图在 java 中制作一个素数计算器,它从 1 到用户给定范围的上限,并打印出所有素数。
解决这个问题的一种方法是简单地使用嵌套 for 循环并测试范围内每个数字是否被小于它的所有数字整除。
不过,我认为只测试质数会更有效,以避免重复因子并加快程序速度。
例如,如果我们的数字是 16,而不是测试它是否可以被 2,3,4,5,...14,15,16 整除,我只能测试 2,3, 5,7,11,13 并在找到一个因子后停止。
所以我尝试创建一个数组来存储到目前为止找到的所有素数,并且只使用这些值来测试下一个数字。
这是我的代码,我不明白为什么它不起作用
Scanner sc = new Scanner (System.in);
System.out.print ("Enter the upper limit: ");
int high = sc.nextInt();
boolean isPrime = true;
int[] primearray = new int[0];
for (int num = 1; num <= high; num++)
{
for (int x: primearray)
{
if (num%x==0)
{
isPrime = false;
break;
}
}
if (isPrime == true) {
int size = primearray.length;
primearray = new int[size+1];
primearray [size] = num;
System.out.println (num);
}
}
首先,1 不是素数。通过从 1 开始你的外循环,你的素数数组以 1 结束,然后其他所有内容都将被测试为不是素数。在 int num = 2
.
其次,当您扩展 primearray
时,您并没有复制现有的已知素数。您可以使用
primearray = Arrays.copyOf(primearray, size+1);
这将创建一个包含所有旧内容的新数组,并 space 多一个值。
最后,您可能想查看 Sieve of Eratosthenes. A careful implementation of that algorithm will be more efficient than your current algorithm, which requires an expensive array reallocation every time you find a prime. You can use a BitSet
以跟踪筛子需要的标志。
正如您所说,关键的简化是仅在找到下一个素数时才测试素数。例如:
public class PrimeGenerator {
private long current = 1;
private final List<Long> primes = new ArrayList<>();
public long next() {
do {
current++;
} while (primes.stream().anyMatch(n -> current % n == 0));
primes.add(current);
return current;
}
public LongStream stream() {
return LongStream.generate(this::next);
}
}
这会记录每个生成的素数。
您可以使用
将所有素数生成为特定值generator.stream().takeWhile(p -> p < value)...
你逻辑有问题
boolean isPrime = true;
这个变量应该在for
循环中声明,想象一下,如果你发现4
不是素数那么isPrime = false
,那么你检查5
但是没有设置 isPrime = true
.
而这个区块:
if (isPrime == true) {
int size = primearray.length;
primearray = new int[size+1];
primearray [size] = num;
System.out.println (num);
}
您创建了新的素数数组,primearray
,大小增加了 1,因此 primearray
不包含任何旧素数,这会在检查素数时出错。所以你需要将旧素数复制到新数组中。
并且因为素数以 2 开头,所以您的代码应该是:
Scanner sc = new Scanner(System.in);
System.out.print("Enter the upper limit: ");
int high = sc.nextInt();
int[] primeArray = new int[0];
for (int num = 2; num <= high; num++)
{
boolean isPrime = true;
for (int x : primeArray)
{
if (num % x == 0)
{
isPrime = false;
break;
}
}
if (isPrime == true)
{
primeArray = Arrays.copyOf(primeArray, primeArray.length + 1);
primeArray[primeArray.length - 1] = num;
System.out.println(num);
}
}
之前的回答已经解释了你的代码有什么问题。
我只想分享另一种更有效的方法。示例实现如下所示。基本上,一旦我们知道 x 是素数,我们也知道 i*x 不是素数。可以进一步阅读和可视化 here
public int countPrimes(int n) {
if (n == 0 || n == 1) return 0;
int count = 0;
boolean[] check = new boolean[n+1];
for (int i = 2; i < n; i++) {
if (check[i]) continue;
for (int j = 1; j <= n / i; j++) {
check[j * i] = true;
}
count++;
}
return count;
}
您应该使用埃拉托色尼筛法来查找素数,而不是测试每个数的可整除性;该方法比筛分慢得多。这是从 my blog:
筛选的实现public static LinkedList sieve(int n)
{
BitSet b = new BitSet(n);
LinkedList ps = new LinkedList();
b.set(0,n);
for (int p=2; p<n; p++)
{
if (b.get(p))
{
ps.add(p);
for (int i=p+p; i<n; i+=p)
{
b.clear(i);
}
}
}
return ps;
}