所以我试图在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);
}
}
答案 0 :(得分:1)
首先,1不是素数。通过将外部循环设置为1,您的素数数组最终以1结尾,然后其他所有内容将测试为非素数。在int num = 2
开始您的外部循环。
其次,当您展开primearray
时,您不会复制现有的已知素数。你可以使用
primearray = Arrays.copyOf(primearray, size+1);
将创建一个新数组,其中复制了所有旧内容,并为另外一个值添加空间。
最后,您可能想查看Sieve of Eratosthenes。仔细实现该算法将比您当前的算法更有效,每次找到素数时都需要昂贵的数组重新分配。您可以使用BitSet
来跟踪筛选所需的标记。
答案 1 :(得分:1)
你的逻辑错误
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);
}
}
答案 2 :(得分:1)
你应该使用Eratosthenes筛子找到素数,而不是测试每个数字的可分性;这种方法远比筛选慢。这是从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;
}
答案 3 :(得分:0)
如你所说,关键的简化是在你找到下一个素数时只测试质数。例如:
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)...
答案 4 :(得分:0)
以前的答案已经解释了您的代码有什么问题。
我只想分享另一种更有效的方法。示例实现如下。基本上,一旦我们知道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;
}